package hostid import ( "bufio" "fmt" "os" "path/filepath" "strconv" "strings" ) // GetRootDiskSerial 返回承载根文件系统的物理磁盘序列号。 func GetRootDiskSerial() (string, error) { // 1. 找 "/" 的 major:minor f, _ := os.Open("/proc/self/mountinfo") defer f.Close() var major, minor int sc := bufio.NewScanner(f) for sc.Scan() { p := strings.Split(sc.Text(), " ") if len(p) > 4 && p[4] == "/" { mm := strings.Split(p[2], ":") major, _ = strconv.Atoi(mm[0]) minor, _ = strconv.Atoi(mm[1]) break } } if major == 0 && minor == 0 { return "", fmt.Errorf("root mount not found") } // 2. /sys/dev/block/major:minor → 解析符号链接,向上回溯直到 // /sys/block/ 存在,适配 nvme0n1p2、mmcblk0p1、dm-0 等所有情况 link := fmt.Sprintf("/sys/dev/block/%d:%d", major, minor) target, err := os.Readlink(link) if err != nil { return "", err } path := filepath.Clean(filepath.Join(filepath.Dir(link), target)) var disk string for { name := filepath.Base(path) // 当前层目录名 if _, err := os.Stat(filepath.Join("/sys/block", name)); err == nil { disk = name // 找到顶层物理盘 break } parent := filepath.Dir(path) if parent == path { // 已到根还没找到 return "", fmt.Errorf("disk name not found via %s", link) } path = parent } // 3. 读取序列号 candidates := []string{ "/sys/block/" + disk + "/device/serial", "/sys/block/" + disk + "/serial", "/sys/block/" + disk + "/device/vpd_unit_serial", "/sys/block/" + disk + "/device/wwid", } for _, p := range candidates { if b, err := os.ReadFile(p); err == nil { s := strings.TrimSpace(string(b)) if s != "" { return s, nil } } } return "", fmt.Errorf("serial not found for %s", disk) }