package ghw import ( "os" "path/filepath" "github.com/kairos-io/kairos-sdk/types" ) type MultipathPartitionHandler struct { DiskName string } func NewMultipathPartitionHandler(diskName string) *MultipathPartitionHandler { return &MultipathPartitionHandler{DiskName: diskName} } var _ PartitionHandler = &MultipathPartitionHandler{} func (m *MultipathPartitionHandler) GetPartitions(paths *Paths, logger *types.KairosLogger) types.PartitionList { out := make(types.PartitionList, 0) // For multipath devices, partitions appear as holders of the parent device // in /sys/block//holders/ holdersPath := filepath.Join(paths.SysBlock, m.DiskName, "holders") logger.Logger.Debug().Str("path", holdersPath).Msg("Reading multipath holders") holders, err := os.ReadDir(holdersPath) if err != nil { logger.Logger.Error().Err(err).Msg("failed to read holders directory") return out } // Find all multipath partitions by checking each holder for _, holder := range holders { partName := holder.Name() // Only consider dm- devices as potential multipath partitions if !isMultipathDevice(paths, holder, logger) { logger.Logger.Debug().Str("path", holder.Name()).Msg("Is not a multipath device") continue } // Verify this holder is actually a multipath partition // We can use the holder DirEntry directly - no need to search for it! if !isMultipathPartition(holder, paths, logger) { logger.Logger.Debug().Str("partition", partName).Msg("Holder is not a multipath partition") continue } logger.Logger.Debug().Str("partition", partName).Msg("Found multipath partition") udevInfo, err := udevInfoPartition(paths, partName, logger) if err != nil { logger.Logger.Error().Err(err).Str("devNo", partName).Msg("Failed to get udev info") return out } mapperName, ok := udevInfo["DM_NAME"] if !ok { logger.Logger.Error().Str("devNo", partName).Msg("DM_NAME not found in udev info") continue } // For multipath partitions, we need to get size directly from the partition device // since it's a top-level entry in /sys/block, not nested under the parent size := partitionSizeBytes(paths, partName, logger) du := diskPartUUID(paths, partName, logger) // The mount point is usually the same as the mapper name // however you can also mount it as /dev/dm- or /dev/mapper/ // so we need to check both potentialMountNames := []string{ filepath.Join("/dev/mapper", mapperName), filepath.Join("/dev", partName), } // Search for the mount point in the system var mp, pt string for _, mountName := range potentialMountNames { mp, pt = partitionInfo(paths, mountName, logger) if mp != "" { logger.Logger.Debug().Str("mountPoint", mp).Msg("Found mount point for partition") break } } if pt == "" { pt = diskPartTypeUdev(paths, partName, logger) } fsLabel := diskFSLabel(paths, partName, logger) p := &types.Partition{ Name: partName, Size: uint(size / (1024 * 1024)), MountPoint: mp, UUID: du, FilesystemLabel: fsLabel, FS: pt, Path: filepath.Join("/dev", partName), Disk: filepath.Join("/dev", m.DiskName), } out = append(out, p) } return out }