| // 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{ | 
 | 			{ | 
 | 				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 | 
 | } |