github.com/jlowellwofford/u-root@v1.0.0/pkg/gpt/gpt.go (about)

     1  // Copyright 2017-2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // gpt implements reading and writing of GUID Partition tables.
     6  // GPTs are dumped in JSON format and written in same.
     7  // One complication is that we frequently only want to
     8  // write a very small subset of a GPT. For example,
     9  // we might only want to change the GUID. As it happens
    10  // it is simpler (and more useful) just to read and write
    11  // the whole thing. In for a penny, in for a pound.
    12  package gpt
    13  
    14  import (
    15  	"bytes"
    16  	"encoding/binary"
    17  	"encoding/json"
    18  	"fmt"
    19  	"hash/crc32"
    20  	"io"
    21  	"log"
    22  )
    23  
    24  const (
    25  	BlockSize         = 512
    26  	HeaderOff         = 0x200
    27  	HeaderSize        = 0x5c               // They claim it can vary. Give me a break.
    28  	Signature  uint64 = 0x5452415020494645 // ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h on little-endian machines)
    29  	Revision          = 0x10000
    30  	MaxNPart          = 0x80
    31  )
    32  
    33  type GUID struct {
    34  	L  uint32
    35  	W1 uint16
    36  	W2 uint16
    37  	B  [8]byte
    38  }
    39  
    40  type MBR [BlockSize]byte
    41  type Header struct {
    42  	Signature  uint64
    43  	Revision   uint32 // (for GPT version 1.0 (through at least UEFI version 2.7 (May 2017)), the value is 00h 00h 01h 00h)
    44  	HeaderSize uint32 // size in little endian (in bytes, usually 5Ch 00h 00h 00h or 92 bytes)
    45  	CRC        uint32 // CRC32/zlib of header (offset +0 up to header size) in little endian, with this field zeroed during calculation
    46  	Reserved   uint32 // ; must be zero
    47  	CurrentLBA uint64 // (location of this header copy)
    48  	BackupLBA  uint64 // (location of the other header copy)
    49  	FirstLBA   uint64 // usable LBA for partitions (primary partition table last LBA + 1)
    50  	LastLBA    uint64 // usable LBA (secondary partition table first LBA - 1)
    51  	DiskGUID   GUID   // (also referred as UUID on UNIXes)
    52  	PartStart  uint64 // LBA of array of partition entries (always 2 in primary copy)
    53  	NPart      uint32 // Number of partition entries in array
    54  	PartSize   uint32 // Size of a single partition entry (usually 80h or 128)
    55  	PartCRC    uint32 // CRC32/zlib of partition array in little endian
    56  }
    57  
    58  type PartAttr uint64
    59  type PartName [72]byte
    60  type Part struct {
    61  	PartGUID   GUID     // Partition type GUID
    62  	UniqueGUID GUID     // Unique partition GUID
    63  	FirstLBA   uint64   // LBA (little endian)
    64  	LastLBA    uint64   // LBA (inclusive, usually odd)
    65  	Attribute  PartAttr // flags (e.g. bit 60 denotes read-only)
    66  	Name       PartName // Partition name (36 UTF-16LE code units)
    67  }
    68  
    69  type GPT struct {
    70  	Header
    71  	Parts []Part
    72  }
    73  
    74  func (g *GUID) String() string {
    75  	return fmt.Sprintf("%08x-%04x-%04x-%02x-%02x", g.L, g.W1, g.W2, g.B[0:2], g.B[2:])
    76  }
    77  
    78  // PartitionTable defines all the partition table information.
    79  // This includes the MBR and two GPTs. The GPTs are
    80  // similar but not identical, as they contain "pointers"
    81  // to each other in the BackupLBA in the Header.
    82  // The design is defective in that if a given Header has
    83  // an error, you are supposed to just assume that the BackupLBA
    84  // is intact, which is a pretty bogus assumption. This is why
    85  // you do standards like this in the open, not in hiding.
    86  // I hope someone from Intel is reading this.
    87  type PartitionTable struct {
    88  	MasterBootRecord *MBR
    89  	Primary          *GPT
    90  	Backup           *GPT
    91  }
    92  
    93  func (m *MBR) String() string {
    94  	b, err := json.MarshalIndent(m, "", "\t")
    95  	if err != nil {
    96  		log.Fatalf("Can't marshal %v", *m)
    97  	}
    98  	return string(b)
    99  }
   100  
   101  func (g *GPT) String() string {
   102  	b, err := json.MarshalIndent(g, "", "\t")
   103  	if err != nil {
   104  		log.Fatalf("Can't marshal %v", *g)
   105  	}
   106  	return string(b)
   107  }
   108  
   109  func (p *PartitionTable) String() string {
   110  	b, err := json.MarshalIndent(p, "", "\t")
   111  	if err != nil {
   112  		log.Fatalf("Can't marshal %v", *p)
   113  	}
   114  	return string(b)
   115  
   116  }
   117  
   118  func errAppend(err error, s string, a ...interface{}) error {
   119  	var p string
   120  	if err != nil {
   121  		p = err.Error() + "; "
   122  	}
   123  	return fmt.Errorf(p+s, a...)
   124  }
   125  
   126  // EqualHeader compares two headers and returns true if they match.
   127  // Those fields which by definition must differ are ignored.
   128  func EqualHeader(p, b Header) error {
   129  	var err error
   130  	if p.Signature != b.Signature {
   131  		err = errAppend(err, "p.Signature(%#x) != b.Signature(%#x)", p.Signature, b.Signature)
   132  	}
   133  	if p.Revision != b.Revision {
   134  		err = errAppend(err, "p.Revision(%v) != b.Revision(%v)", p.Revision, b.Revision)
   135  	}
   136  	if p.HeaderSize != b.HeaderSize {
   137  		err = errAppend(err, "p.HeaderSize(%v) != b.HeaderSize(%v)", p.HeaderSize, b.HeaderSize)
   138  	}
   139  	if p.CurrentLBA != b.BackupLBA {
   140  		err = errAppend(err, "p.CurrentLBA(%#x) != b.BackupLBA(%#x)", p.CurrentLBA, b.BackupLBA)
   141  	}
   142  	if p.BackupLBA != b.CurrentLBA {
   143  		err = errAppend(err, "p.BackupLBA(%#x) != b.CurrentLBA(%#x)", p.BackupLBA, b.CurrentLBA)
   144  	}
   145  	if p.FirstLBA != b.FirstLBA {
   146  		err = errAppend(err, "p.FirstLBA(%#x) != b.FirstLBA(%#x)", p.FirstLBA, b.FirstLBA)
   147  	}
   148  	if p.LastLBA != b.LastLBA {
   149  		err = errAppend(err, "p.LastLBA(%#x) != b.LastLBA(%#x)", p.LastLBA, b.LastLBA)
   150  	}
   151  	if p.DiskGUID != b.DiskGUID {
   152  		err = errAppend(err, "p.DiskGUID(%#x) != b.DiskGUID(%#x)", p.DiskGUID, b.DiskGUID)
   153  	}
   154  	if p.NPart != b.NPart {
   155  		err = errAppend(err, "p.NPart(%v) != b.NPart(%v)", p.NPart, b.NPart)
   156  	}
   157  	if p.PartSize != b.PartSize {
   158  		err = errAppend(err, "p.PartSize(%v) != b.PartSize(%v)", p.PartSize, b.PartSize)
   159  	}
   160  	return err
   161  }
   162  
   163  func EqualPart(p, b Part) (err error) {
   164  	if p.PartGUID != b.PartGUID {
   165  		err = errAppend(err, "p.PartGUID(%#x) != b.PartGUID(%#x)", p.PartGUID, b.PartGUID)
   166  	}
   167  	if p.UniqueGUID != b.UniqueGUID {
   168  		err = errAppend(err, "p.UniqueGUID(%#x) != b.UniqueGUID(%#x)", p.UniqueGUID, b.UniqueGUID)
   169  	}
   170  	if p.FirstLBA != b.FirstLBA {
   171  		err = errAppend(err, "p.FirstLBA(%#x) != b.FirstLBA(%#x)", p.FirstLBA, b.FirstLBA)
   172  	}
   173  	if p.LastLBA != b.LastLBA {
   174  		err = errAppend(err, "p.LastLBA(%#x) != b.LastLBA(%#x)", p.LastLBA, b.LastLBA)
   175  	}
   176  	// TODO: figure out what Attributes actually matter. We're not able to tell what differences
   177  	// matter and what differences don't.
   178  	if false && p.Attribute != b.Attribute {
   179  		err = errAppend(err, "p.Attribute(%#x) != b.Attribute(%#x)", p.Attribute, b.Attribute)
   180  	}
   181  	if p.Name != b.Name {
   182  		err = errAppend(err, "p.Name(%#x) != b.Name(%#x)", p.Name, b.Name)
   183  	}
   184  	return err
   185  }
   186  
   187  // EqualParts compares the Parts arrays from two GPTs
   188  // and returns an error if they differ.
   189  // If they length differs we just give up, since there's no way
   190  // to know which should have matched.
   191  // Otherwise, we do a 1:1 comparison.
   192  func EqualParts(p, b *GPT) (err error) {
   193  	if len(p.Parts) != len(b.Parts) {
   194  		return fmt.Errorf("Primary Number of partitions (%d) differs from Backup (%d)", len(p.Parts), len(b.Parts))
   195  	}
   196  	for i := range p.Parts {
   197  		if e := EqualPart(p.Parts[i], b.Parts[i]); e != nil {
   198  			err = errAppend(err, "Partition %d: %v", i, e)
   199  		}
   200  	}
   201  	return err
   202  }
   203  
   204  // Table reads a GPT table at the given offset.  It checks that
   205  // the Signature, Revision, HeaderSize, and MaxPart are reasonable. It
   206  // also verifies the header and partition table CRC32 values.
   207  func Table(r io.ReaderAt, off int64) (*GPT, error) {
   208  	which := "Primary"
   209  	if off != BlockSize {
   210  		which = "Backup"
   211  	}
   212  	var g = &GPT{}
   213  	if err := binary.Read(io.NewSectionReader(r, off, HeaderSize), binary.LittleEndian, &g.Header); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	if g.Signature != Signature {
   218  		return nil, fmt.Errorf("%s GPT signature invalid (%x), needs to be %x", which, g.Signature, Signature)
   219  	}
   220  	if g.Revision != Revision {
   221  		return nil, fmt.Errorf("%s GPT revision (%x) is not supported value (%x)", which, g.Revision, Revision)
   222  	}
   223  	if g.HeaderSize != HeaderSize {
   224  		return nil, fmt.Errorf("%s GPT HeaderSize (%x) is not supported value (%x)", which, g.HeaderSize, HeaderSize)
   225  	}
   226  	if g.NPart > MaxNPart {
   227  		return nil, fmt.Errorf("%s GPT MaxNPart (%x) is above maximum of %x", which, g.NPart, MaxNPart)
   228  	}
   229  
   230  	// Read in all the partition data and check the hash.
   231  	// Since the partition hash is included in the header CRC,
   232  	// it's sensible to check it first.
   233  	s := int64(g.PartSize)
   234  	partBlocks := make([]byte, int64(g.NPart)*s)
   235  	n, err := r.ReadAt(partBlocks, int64(g.PartStart)*BlockSize)
   236  	if n != len(partBlocks) || err != nil {
   237  		return nil, fmt.Errorf("%s Reading partitions: Wanted %d bytes, got %d: %v", which, n, len(partBlocks), err)
   238  	}
   239  	if h := crc32.ChecksumIEEE(partBlocks); h != g.PartCRC {
   240  		return g, fmt.Errorf("%s Partition CRC: Header %v, computed checksum is %08x, header has %08x", which, g, h, g.PartCRC)
   241  	}
   242  
   243  	hdr := make([]byte, g.HeaderSize)
   244  	n, err = r.ReadAt(hdr, off)
   245  	if n != len(hdr) || err != nil {
   246  		return nil, fmt.Errorf("%s Reading Header: Wanted %d bytes, got %d: %v", which, n, len(hdr), err)
   247  	}
   248  	// Zap the checksum in the header to 0.
   249  	copy(hdr[16:], []byte{0, 0, 0, 0})
   250  	if h := crc32.ChecksumIEEE(hdr); h != g.CRC {
   251  		return g, fmt.Errorf("%s Header CRC: computed checksum is %08x, header has %08x", which, h, g.CRC)
   252  	}
   253  
   254  	// Now read in the partition table entries.
   255  	g.Parts = make([]Part, g.NPart)
   256  
   257  	for i := range g.Parts {
   258  		if err := binary.Read(io.NewSectionReader(r, int64(g.PartStart*BlockSize)+int64(i)*s, s), binary.LittleEndian, &g.Parts[i]); err != nil {
   259  			return nil, fmt.Errorf("%s GPT partition %d failed: %v", which, i, err)
   260  		}
   261  	}
   262  
   263  	return g, nil
   264  
   265  }
   266  
   267  // Write writes the MBR and primary and backup GPTs to w.
   268  func Write(w io.WriterAt, p *PartitionTable) error {
   269  	if _, err := w.WriteAt(p.MasterBootRecord[:], 0); err != nil {
   270  		return err
   271  	}
   272  	if err := writeGPT(w, p.Primary); err != nil {
   273  		return err
   274  	}
   275  
   276  	if err := writeGPT(w, p.Backup); err != nil {
   277  		return err
   278  	}
   279  	return nil
   280  
   281  }
   282  
   283  // Write writes the GPT to w. It generates the partition and header CRC before writing.
   284  func writeGPT(w io.WriterAt, g *GPT) error {
   285  	// The maximum extent is NPart * PartSize
   286  	var h = make([]byte, uint64(g.NPart*g.PartSize))
   287  	s := int64(g.PartSize)
   288  	for i := int64(0); i < int64(g.NPart); i++ {
   289  		var b bytes.Buffer
   290  		if err := binary.Write(&b, binary.LittleEndian, &g.Parts[i]); err != nil {
   291  			return err
   292  		}
   293  		copy(h[i*s:], b.Bytes())
   294  	}
   295  
   296  	ps := int64(g.PartStart * BlockSize)
   297  	if _, err := w.WriteAt(h, ps); err != nil {
   298  		return fmt.Errorf("Writing %d bytes of partition table at %v: %v", len(h), ps, err)
   299  	}
   300  
   301  	g.PartCRC = crc32.ChecksumIEEE(h[:])
   302  	g.CRC = 0
   303  	var b bytes.Buffer
   304  	if err := binary.Write(&b, binary.LittleEndian, &g.Header); err != nil {
   305  		return err
   306  	}
   307  	h = make([]byte, g.HeaderSize)
   308  	copy(h, b.Bytes())
   309  	g.CRC = crc32.ChecksumIEEE(h[:])
   310  	b.Reset()
   311  	if err := binary.Write(&b, binary.LittleEndian, g.CRC); err != nil {
   312  		return err
   313  	}
   314  	copy(h[16:], b.Bytes())
   315  
   316  	_, err := w.WriteAt(h, int64(g.CurrentLBA*BlockSize))
   317  	return err
   318  }
   319  
   320  // New reads in the MBR, primary and backup GPT from a disk and returns a pointer to them.
   321  // There are some checks it can apply. It can return with a
   322  // one or more headers AND an error. Sorry. Experience with some real USB sticks
   323  // is showing that we need to return data even if there are some things wrong.
   324  func New(r io.ReaderAt) (*PartitionTable, error) {
   325  	var p = &PartitionTable{}
   326  	var mbr = &MBR{}
   327  	n, err := r.ReadAt(mbr[:], 0)
   328  	if n != BlockSize || err != nil {
   329  		return p, err
   330  	}
   331  	p.MasterBootRecord = mbr
   332  	g, err := Table(r, HeaderOff)
   333  	// If we can't read the table it's kinda hard to find the backup.
   334  	// Bit of a flaw in the design, eh?
   335  	// "We can't recover the backup from the error with the primary because there
   336  	// was an error in the primary"
   337  	// uh, what?
   338  	if err != nil {
   339  		return p, err
   340  	}
   341  	p.Primary = g
   342  
   343  	b, err := Table(r, int64(g.BackupLBA*BlockSize))
   344  	if err != nil {
   345  		// you go to war with the army you have
   346  		return p, err
   347  	}
   348  
   349  	if err := EqualHeader(g.Header, b.Header); err != nil {
   350  		return p, fmt.Errorf("Primary GPT and backup GPT Header differ: %v", err)
   351  	}
   352  
   353  	if g.CRC == b.CRC {
   354  		return p, fmt.Errorf("Primary (%v) and Backup (%v) Header CRC (%x) are the same and should differ", g.Header, b.Header, g.CRC)
   355  	}
   356  
   357  	p.Backup = b
   358  	return p, EqualParts(g, b)
   359  }