blob: 98261ef6d738455e3064b9e73f5d9488b1239b46 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package mbr is a minimial implementation of Master Boot Record parsing and
// writing, implemented in support of GUID Partition Table parsing and writing.
// It is based on the UEFI Specification v2.6.
package mbr
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
// OSType marks the partition type in an MBR Partition Record
//go:generate stringer -type=OSType
type OSType byte
// TODO(raggi): fill in other types and organize from: "Partition types" by
// Andries Brouwer: See "Links to UEFI-Related Documents" (http://uefi.org/uefi)
// under the heading "OS Type values used in the MBR disk layout".
const (
FAT32 OSType = 0x0b
UEFISystemPartition OSType = 0xEF
GPTProtective OSType = 0xEE
)
// Signature is an MBR Signature as two bytes in little endian.
type Signature uint16
//go:generate stringer -type=Signature
const (
// GPTSignature is the partition signature of a protective MBR partition
GPTSignature Signature = 0xAA55
)
// PartitionRecord is a type representing a legacy MBR partition record
type PartitionRecord struct {
BootIndicator byte
StartingCHS [3]byte
OSType OSType
EndingCHS [3]byte
StartingLBA uint32
SizeInLBA uint32
}
// MBRSize is the size of an MBR record without logical block size padding
const MBRSize = 512
// MBR is a Go representation of a legacy Master Boot Record.
type MBR struct {
BootCode [424]byte // 424 bytes should be enough for anybody...
Pad [16]byte // unnamed blank space in efi spec?
UniqueMBRDiskSignature uint32
Unknown [2]byte
PartitionRecord [4]PartitionRecord
Signature Signature
}
// ReadFrom reads from the given io.Reader into the receiver MBR. If an error
// occurs the returned bytes read may be incorrect.
func (m *MBR) ReadFrom(r io.Reader) (int64, error) {
return MBRSize, binary.Read(r, binary.LittleEndian, m)
}
// WriteTo implements the io.WriterTo interface for MBR. It writes the MBR to
// w in little endian as per the GPT specification.
func (m *MBR) WriteTo(w io.Writer) (int64, error) {
return MBRSize, binary.Write(w, binary.LittleEndian, m)
}
func (m MBR) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "Disk Signature: 0x%X\nSignature: %s\n",
m.UniqueMBRDiskSignature, m.Signature)
for i, p := range m.PartitionRecord {
if p.OSType == OSType(0) && p.StartingLBA == 0 {
continue
} else if i > 0 {
b.Write([]byte("\n"))
}
fmt.Fprintf(&b,
"Boot Indicator: 0x%X\nOS Type: %s\nStarting LBA: 0x%X\nSize In LBA: 0x%X (%d)",
p.BootIndicator, p.OSType, p.StartingLBA, p.SizeInLBA, p.SizeInLBA)
}
return b.String()
}
// WriteProtectiveMBR constructs and writes a protective MBR to w. It will only
// write the first block to w (that is, blockSize bytes).
func WriteProtectiveMBR(w io.Writer, blockSize, numBlocks uint64) error {
m := NewProtectiveMBR(numBlocks)
_, err := m.WriteTo(w)
if err != nil {
return err
}
// TODO(raggi): consider doing this without allocating all that space
_, err = w.Write(make([]byte, blockSize-MBRSize))
return err
}
// NewProtectiveMBR constructs an MBR struct with fields conformant to the UEFI
// specification 2.6 "Protective MBR"
func NewProtectiveMBR(numBlocks uint64) MBR {
var partSizeInLBA32 uint32 = 0xffffffff
if numBlocks < uint64(partSizeInLBA32) {
partSizeInLBA32 = uint32(numBlocks) - 1
}
return MBR{
PartitionRecord: [4]PartitionRecord{
PartitionRecord{
OSType: GPTProtective,
StartingCHS: [3]byte{0, 2, 0},
// TODO(raggi): correctly calculate endingCHS.
StartingLBA: 1,
SizeInLBA: partSizeInLBA32,
},
},
Signature: GPTSignature,
}
}
// ReadMBR reads a MasterBootRecord from r.
func ReadMBR(r io.Reader) (*MBR, error) {
m := &MBR{}
_, err := m.ReadFrom(r)
return m, err
}