diff --git a/base.go b/base.go index 239da4c..5b8e5b5 100644 --- a/base.go +++ b/base.go @@ -30,6 +30,8 @@ const EndOfEntireDevicePath DeviceNodeSubType = 0xFF const ACPISubType DeviceNodeSubType = 1 +const HardDriveSubType DeviceNodeSubType = 1 + type DevicePathNode interface { Type() DeviceNodeType SubType() DeviceNodeSubType diff --git a/go.mod b/go.mod index 0d690c3..6deb360 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,10 @@ module git.faercol.me/faercol/devicepath go 1.20 -require github.com/stretchr/testify v1.8.4 +require ( + github.com/google/uuid v1.3.1 + github.com/stretchr/testify v1.8.4 +) require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index fa4b6e6..e70524c 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/harddrive.go b/harddrive.go new file mode 100644 index 0000000..f2aa503 --- /dev/null +++ b/harddrive.go @@ -0,0 +1,105 @@ +package devicepath + +import ( + "errors" + "fmt" + "strconv" + "strings" + + "github.com/google/uuid" +) + +const ( + hdLength uint16 = 2 + hdPrefix string = "HD" +) + +func signatureTypeToStr(val uint8) string { + switch val { + case 1: + return "MBR" + default: + return "GPT" + } +} + +func formatSignature(signature [16]byte, signatureType uint8) string { + switch signatureType { + case 1: // MBR + return fmt.Sprintf("%x", signature[:4]) // only stored on the first 4 bytes + default: // default is GPT + parsedUID, _ := uuid.FromBytes(signature[:]) + return parsedUID.String() + } +} + +type HardDriveDevicePath struct { + partitionNumber uint32 + partitionStart uint64 + partitionSize uint64 + partitionSignature [16]byte + // partitionFormat uint8 + signatureType uint8 +} + +func (hp *HardDriveDevicePath) Type() DeviceNodeType { + return MediaDevicePath +} + +func (hp *HardDriveDevicePath) SubType() DeviceNodeSubType { + return HardDriveSubType +} + +func (hp *HardDriveDevicePath) String() string { + return fmt.Sprintf("%s(%d,%s,%s,0x%x,0x%x)", hdPrefix, hp.partitionNumber, signatureTypeToStr(hp.signatureType), formatSignature(hp.partitionSignature, hp.signatureType), hp.partitionStart, hp.partitionSize) +} + +func (hp *HardDriveDevicePath) Bytes() []byte { + return nil +} + +func (hp *HardDriveDevicePath) ParseString(raw string) error { + if !checkStringFormat(raw, hdPrefix, -1) { + return ErrMalformedString + } + args := strings.Split(raw[len(hdPrefix)+1:len(raw)-1], ",") + if len(args) != 5 { + return errors.New("unexpected number of arguments") + } + + if partNumber, err := strconv.ParseUint(args[0], 10, 32); err != nil { + return err + } else { + hp.partitionNumber = uint32(partNumber) + } + + switch args[1] { + case "MBR": + return errors.New("MBR partitions not yet supported") + default: + hp.signatureType = 2 + } + + if uid, err := uuid.Parse(args[2]); err != nil { + return err + } else { + if dat, err := uid.MarshalBinary(); err != nil { + return err + } else { + hp.partitionSignature = [16]byte(dat) + } + } + + if start, err := strconv.ParseUint(args[3], 0, 64); err != nil { + return err + } else { + hp.partitionStart = start + } + + if size, err := strconv.ParseUint(args[4], 0, 64); err != nil { + return err + } else { + hp.partitionSize = size + } + return nil +} diff --git a/harddrive_test.go b/harddrive_test.go new file mode 100644 index 0000000..4f2bc39 --- /dev/null +++ b/harddrive_test.go @@ -0,0 +1,18 @@ +package devicepath_test + +import ( + "testing" + + "git.faercol.me/faercol/devicepath" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHDParseString(t *testing.T) { + t.Run("GPT", func(t *testing.T) { + expected := "HD(1,GPT,16f06d01-50da-6544-86bd-f3457f980086,0x1000,0x96000)" + var dev devicepath.HardDriveDevicePath + require.NoError(t, dev.ParseString(expected)) + assert.Equal(t, expected, dev.String()) + }) +} diff --git a/parser.go b/parser.go index 1a92ab5..c7ac1c7 100644 --- a/parser.go +++ b/parser.go @@ -34,6 +34,7 @@ var prefixTypeAssociation = map[string]nodeGenerator{ sataPrefix: func() DevicePathNode { return &SataDevicePath{} }, pciPrefix: func() DevicePathNode { return &PCIDevicePath{} }, pciRootPrefix: func() DevicePathNode { return &PCIRootDevicePath{} }, + hdPrefix: func() DevicePathNode { return &HardDriveDevicePath{} }, } func Parsenode(raw string) (DevicePathNode, error) {