diff --git a/ghw/ghw.go b/ghw/ghw.go index ec0bb4a..790ae4a 100644 --- a/ghw/ghw.go +++ b/ghw/ghw.go @@ -47,8 +47,41 @@ func NewPaths(withOptionalPrefix string) *Paths { return p } -func isMultipathDevice(entry os.DirEntry) bool { - return strings.HasPrefix(entry.Name(), "dm-") +func isMultipathDevice(paths *Paths, entry os.DirEntry, logger *types.KairosLogger) bool { + hasPrefix := strings.HasPrefix(entry.Name(), "dm-") + if !hasPrefix { + return false + } + + // Check if the device has a "slaves" directory, which is a common indicator + _, err := os.Stat(filepath.Join(paths.SysBlock, entry.Name(), "slaves")) + if err != nil { + var msg string + if os.IsNotExist(err) { + msg = "No slaves directory, not a multipath device" + } else { + msg = "Error checking slaves directory" + } + + logger.Logger.Debug().Str("devNo", entry.Name()).Msg(msg) + return false + } + + // If the device has a "slaves" directory, we can check its udev info + // to confirm it's a multipath device. + // This is a more reliable check than just the name. + udevInfo, err := udevInfoPartition(paths, entry.Name(), logger) + if err != nil { + logger.Logger.Error().Err(err).Str("devNo", entry.Name()).Msg("Failed to get udev info") + return false + } + // Check if the udev info contains DM_NAME indicating it's a multipath device + _, ok := udevInfo["DM_NAME"] + if !ok { + logger.Logger.Debug().Str("devNo", entry.Name()).Msg("Not a multipath device") + } + + return ok } func GetDisks(paths *Paths, logger *types.KairosLogger) []*types.Disk { @@ -85,7 +118,7 @@ func GetDisks(paths *Paths, logger *types.KairosLogger) []*types.Disk { UUID: diskUUID(paths, dname, logger), } - if(isMultipathDevice(file)) { + if(isMultipathDevice(paths, file, logger)) { partitionHandler = NewMultipathPartitionHandler(dname) } else { partitionHandler = NewDiskPartitionHandler(dname) @@ -103,7 +136,7 @@ func GetDisks(paths *Paths, logger *types.KairosLogger) []*types.Disk { func isMultipathPartition(entry os.DirEntry, paths *Paths, logger *types.KairosLogger) bool { // Must be a dm device to be a multipath partition - if !isMultipathDevice(entry) { + if !isMultipathDevice(paths, entry, logger) { return false } diff --git a/ghw/mocks/ghw_mock.go b/ghw/mocks/ghw_mock.go index 41c3414..c9e9a89 100644 --- a/ghw/mocks/ghw_mock.go +++ b/ghw/mocks/ghw_mock.go @@ -67,7 +67,18 @@ func (g *GhwMock) CreateDevices() { // Also write the size _ = os.WriteFile(filepath.Join(g.paths.SysBlock, disk.Name, "size"), []byte(strconv.FormatUint(disk.SizeBytes, 10)), 0644) // Create the udevdata for this disk - _ = os.WriteFile(filepath.Join(g.paths.RunUdevData, fmt.Sprintf("b%d:0", indexDisk)), []byte(fmt.Sprintf("E:ID_PART_TABLE_UUID=%s\n", disk.UUID)), 0644) + var diskUdevData []string + diskUdevData = append(diskUdevData, fmt.Sprintf("E:ID_PART_TABLE_UUID=%s\n", disk.UUID)) + + // Add DM_NAME for dm devices (needed for isMultipathDevice detection) + if strings.HasPrefix(disk.Name, "dm-") { + diskUdevData = append(diskUdevData, fmt.Sprintf("E:DM_NAME=%s\n", disk.Name)) + diskUdevData = append(diskUdevData, "E:DM_SUSPENDED=0\n") + diskUdevData = append(diskUdevData, "E:DM_UDEV_RULES=1\n") + diskUdevData = append(diskUdevData, "E:SUBSYSTEM=block\n") + } + + _ = os.WriteFile(filepath.Join(g.paths.RunUdevData, fmt.Sprintf("b%d:0", indexDisk)), []byte(strings.Join(diskUdevData, "")), 0644) for indexPart, partition := range disk.Partitions { // For each partition we create the /sys/block/DISK_NAME/PARTITION_NAME _ = os.Mkdir(filepath.Join(diskPath, partition.Name), 0755) @@ -249,6 +260,11 @@ func (g *GhwMock) createMultipathPartitionWithMountFormat(parentDiskName string, _ = os.WriteFile(filepath.Join(partDmDir, "name"), []byte(fmt.Sprintf("%s%s", parentDiskName, partitionSuffix)), 0644) _ = os.WriteFile(filepath.Join(partDmDir, "uuid"), []byte(fmt.Sprintf("part-mpath-%s", partition.UUID)), 0644) + // Create slaves directory for partition pointing to parent + partSlavesDir := filepath.Join(partitionPath, "slaves") + _ = os.MkdirAll(partSlavesDir, 0755) + _ = os.WriteFile(filepath.Join(partSlavesDir, parentDiskName), []byte(""), 0644) + // Create holder symlink from parent to partition _ = os.WriteFile(filepath.Join(holdersDir, partition.Name), []byte(""), 0644) @@ -327,6 +343,11 @@ func (g *GhwMock) AddMultipathPartition(parentDiskName string, partition *types. _ = os.WriteFile(filepath.Join(partDmDir, "name"), []byte(fmt.Sprintf("%s%s", parentDiskName, partitionSuffix)), 0644) _ = os.WriteFile(filepath.Join(partDmDir, "uuid"), []byte(fmt.Sprintf("part-mpath-%s", partition.UUID)), 0644) + // Create slaves directory for partition pointing to parent + partSlavesDir := filepath.Join(partitionPath, "slaves") + _ = os.MkdirAll(partSlavesDir, 0755) + _ = os.WriteFile(filepath.Join(partSlavesDir, parentDiskName), []byte(""), 0644) + // Create holder symlink from parent to partition _ = os.WriteFile(filepath.Join(holdersDir, partition.Name), []byte(""), 0644) diff --git a/ghw/multipath_partition_hander.go b/ghw/multipath_partition_hander.go index d4ca6d3..4e66097 100644 --- a/ghw/multipath_partition_hander.go +++ b/ghw/multipath_partition_hander.go @@ -36,8 +36,8 @@ func (m *MultipathPartitionHandler) GetPartitions(paths *Paths, logger *types.Ka partName := holder.Name() // Only consider dm- devices as potential multipath partitions - if !isMultipathDevice(holder) { - logger.Logger.Debug().Str("path", holder.Name()).Msg("Is not a multipath device") + if !isMultipathDevice(paths, holder, logger) { + logger.Logger.Debug().Str("path", holder.Name()).Msg("Is not a multipath device") continue }