Files
internal/gpt/gpt.go
Michael Stapelberg 68dd68393b gpt: fix test
2024-05-10 18:55:00 +02:00

88 lines
2.3 KiB
Go

// Package gpt provides a minimal reader for partition tables in GPT (GUID
// partition tables) format, just enough for the rootdev package to match block
// devices to root=PARTUUID= kernel parameters.
package gpt
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
type PartitionEntry struct {
TypeGUID [16]byte
GUID [16]byte
FirstLBA uint64
LastLBA uint64
Attributes uint64
Name [72]byte
}
func readPartitionEntries(r io.Reader) ([]PartitionEntry, error) {
// 512 bytes MBR
// 512 bytes GPT header
// 512 bytes GPT partition entries
buf := make([]byte, 3*512)
if _, err := r.Read(buf); err != nil {
return nil, err
}
// TODO: gokrazy always writes exactly 4 partitions, but it would be better
// to detect the number of partitions
parts := make([]PartitionEntry, 4)
rd := bytes.NewReader(buf[2*512:])
for idx := range parts {
if err := binary.Read(rd, binary.LittleEndian, &parts[idx]); err != nil {
return nil, err
}
}
return parts, nil
}
// PartitionEntries returns all GPT partition entries on the disk.
func PartitionEntries(r io.Reader) ([]PartitionEntry, error) {
return readPartitionEntries(r)
}
// PartitionUUIDs returns the ids of all GPT partitions on the disk.
func PartitionUUIDs(r io.Reader) []string {
parts, err := readPartitionEntries(r)
if err != nil {
return nil
}
uuids := make([]string, len(parts))
for idx, part := range parts {
uuids[idx] = GUIDFromBytes(part.GUID[:])
}
return uuids
}
// GUIDFromBytes returns the canonical string representation of the specified
// GUID.
func GUIDFromBytes(b []byte) string {
// See Intel EFI specification, Appendix A: GUID and Time Formats
// https://www.intel.de/content/dam/doc/product-specification/efi-v1-10-specification.pdf
var (
timeLow uint32
timeMid uint16
timeHighAndVersion uint16
clockSeqHighAndReserved uint8
clockSeqLow uint8
node [6]byte
)
timeLow = binary.LittleEndian.Uint32(b[0:4])
timeMid = binary.LittleEndian.Uint16(b[4:6])
timeHighAndVersion = binary.LittleEndian.Uint16(b[6:8])
clockSeqHighAndReserved = b[8]
clockSeqLow = b[9]
copy(node[:], b[10:])
return fmt.Sprintf("%08X-%04X-%04X-%02X%02X-%012X",
timeLow,
timeMid,
timeHighAndVersion,
clockSeqHighAndReserved,
clockSeqLow,
node)
}