//go:build linux package console import ( "crypto/sha256" "fmt" "os" "syscall" "unsafe" "github.com/carlosframework/platform/internal/hostops" ) // Linux's FS_IOC_GETFSUUID returns the external filesystem UUID, not the // boot-local device number or the truncated statfs fsid. The ABI is the // 17-byte fsuuid2 from linux/fs.h: one length byte and sixteen UUID bytes. const sqlGetFilesystemUUID = 0x80111500 // Tests replace only the filesystem probe on ephemeral tmpfs. Production // never substitutes a configured or randomly generated filesystem identity. var sqlFilesystemUUID = readSQLFilesystemUUID func readSQLFilesystemUUID(fd uintptr) ([16]byte, error) { var zero [16]byte var fs syscall.Statfs_t if err := syscall.Fstatfs(int(fd), &fs); err != nil { return zero, err } if !sqlFilesystemSupported(int64(fs.Type)) { return zero, hostops.ErrContract } var out struct { Length byte UUID [16]byte } _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, sqlGetFilesystemUUID, uintptr(unsafe.Pointer(&out))) if errno != 0 { return zero, errno } if out.Length != 16 || out.UUID == zero { return zero, hostops.ErrIdentity } return out.UUID, nil } // sqlFilesystemSupported names the filesystems whose FS_IOC_GETFSUUID // (Linux 6.5+) returns a persistent UUID: ext-family, XFS and btrfs. tmpfs // is refused because its receipts do not survive boot. func sqlFilesystemSupported(fsType int64) bool { switch fsType { case 0xef53, 0x58465342, 0x9123683e: // ext-family, XFS, btrfs return true } return false } func sqlPhysicalIdentity(path string) (string, error) { fd, err := syscall.Open(path, syscall.O_RDONLY|syscall.O_CLOEXEC|syscall.O_NOFOLLOW, 0) if err != nil { return "", err } f := os.NewFile(uintptr(fd), path) defer f.Close() var st syscall.Stat_t if err = syscall.Fstat(fd, &st); err != nil { return "", err } if st.Mode&syscall.S_IFMT != syscall.S_IFREG { return "", hostops.ErrIdentity } uuid, err := sqlFilesystemUUID(f.Fd()) if err != nil { return "", err } machine, err := os.ReadFile("/etc/machine-id") if err != nil { return "", err } return sqlPhysicalIdentityFrom(machine, uuid, st) } func sqlPhysicalIdentityFrom(machine []byte, uuid [16]byte, st syscall.Stat_t) (string, error) { if len(machine) == 0 || uuid == ([16]byte{}) || st.Ino == 0 { return "", hostops.ErrIdentity } // st.Dev is deliberately absent: NVMe/device-mapper renumbering does not // change the original filesystem or its inode across a console restart. return fmt.Sprintf("fsuuid-v1:%x:%x:%d", sha256.Sum256(machine), uuid, st.Ino), nil }