plugai_updsrv/internal/hostid/disk.go

73 lines
1.8 KiB
Go

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/<disk> 存在,适配 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)
}