github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/pkg/gpt/gpt.go (about)

     1  // Copyright 2009-2017 The Go 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  	"github.com/google/uuid"
    24  )
    25  
    26  const (
    27  	BlockSize         = 512
    28  	HeaderOff         = 0x200
    29  	HeaderSize        = 0x5c               // They claim it can vary. Give me a break.
    30  	Signature  uint64 = 0x5452415020494645 // ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h on little-endian machines)
    31  	Revision          = 0x10000
    32  	MaxNPart          = 0x80
    33  )
    34  
    35  type Header struct {
    36  	Signature  uint64
    37  	Revision   uint32    // (for GPT version 1.0 (through at least UEFI version 2.7 (May 2017)), the value is 00h 00h 01h 00h)
    38  	HeaderSize uint32    // size in little endian (in bytes, usually 5Ch 00h 00h 00h or 92 bytes)
    39  	CRC        uint32    // CRC32/zlib of header (offset +0 up to header size) in little endian, with this field zeroed during calculation
    40  	Reserved   uint32    // ; must be zero
    41  	CurrentLBA uint64    // (location of this header copy)
    42  	BackupLBA  uint64    // (location of the other header copy)
    43  	FirstLBA   uint64    // usable LBA for partitions (primary partition table last LBA + 1)
    44  	LastLBA    uint64    // usable LBA (secondary partition table first LBA - 1)
    45  	DiskGUID   uuid.UUID // (also referred as UUID on UNIXes)
    46  	PartStart  uint64    // LBA of array of partition entries (always 2 in primary copy)
    47  	NPart      uint32    // Number of partition entries in array
    48  	PartSize   uint32    // Size of a single partition entry (usually 80h or 128)
    49  	PartCRC    uint32    // CRC32/zlib of partition array in little endian
    50  }
    51  
    52  type PartAttr uint64
    53  type PartName [72]byte
    54  type Part struct {
    55  	PartGUID   uuid.UUID // Partition type GUID
    56  	UniqueGUID uuid.UUID // Unique partition GUID
    57  	FirstLBA   uint64    // LBA (little endian)
    58  	LastLBA    uint64    // LBA (inclusive, usually odd)
    59  	Attribute  PartAttr  // flags (e.g. bit 60 denotes read-only)
    60  	Name       PartName  // Partition name (36 UTF-16LE code units)
    61  }
    62  
    63  type GPT struct {
    64  	Header
    65  	Parts []Part
    66  }
    67  
    68  func (g *GPT) String() string {
    69  	b, err := json.MarshalIndent(g, "", "\t")
    70  	if err != nil {
    71  		log.Fatalf("Can't marshal %v", *g)
    72  	}
    73  	return string(b)
    74  }
    75  
    76  func errAppend(err error, s string, a ...interface{}) error {
    77  	var p string
    78  	if err != nil {
    79  		p = err.Error() + "; "
    80  	}
    81  	return fmt.Errorf(p+s, a...)
    82  }
    83  
    84  // EqualHeader compares two headers and returns true if they match.
    85  // Those fields which by definition must differ are ignored.
    86  func EqualHeader(p, b Header) error {
    87  	var err error
    88  	if p.Signature != b.Signature {
    89  		err = errAppend(err, "p.Signature(%#x) != b.Signature(%#x)", p.Signature, b.Signature)
    90  	}
    91  	if p.Revision != b.Revision {
    92  		err = errAppend(err, "p.Revision(%v) != b.Revision(%v)", p.Revision, b.Revision)
    93  	}
    94  	if p.HeaderSize != b.HeaderSize {
    95  		err = errAppend(err, "p.HeaderSize(%v) != b.HeaderSize(%v)", p.HeaderSize, b.HeaderSize)
    96  	}
    97  	if p.CurrentLBA != b.BackupLBA {
    98  		err = errAppend(err, "p.CurrentLBA(%#x) != b.BackupLBA(%#x)", p.CurrentLBA, b.BackupLBA)
    99  	}
   100  	if p.BackupLBA != b.CurrentLBA {
   101  		err = errAppend(err, "p.BackupLBA(%#x) != b.CurrentLBA(%#x)", p.BackupLBA, b.CurrentLBA)
   102  	}
   103  	if p.FirstLBA != b.FirstLBA {
   104  		err = errAppend(err, "p.FirstLBA(%#x) != b.FirstLBA(%#x)", p.FirstLBA, b.FirstLBA)
   105  	}
   106  	if p.LastLBA != b.LastLBA {
   107  		err = errAppend(err, "p.LastLBA(%#x) != b.LastLBA(%#x)", p.LastLBA, b.LastLBA)
   108  	}
   109  	if p.DiskGUID != b.DiskGUID {
   110  		err = errAppend(err, "p.DiskGUID(%#x) != b.DiskGUID(%#x)", p.DiskGUID, b.DiskGUID)
   111  	}
   112  	if p.NPart != b.NPart {
   113  		err = errAppend(err, "p.NPart(%v) != b.NPart(%v)", p.NPart, b.NPart)
   114  	}
   115  	if p.PartSize != b.PartSize {
   116  		err = errAppend(err, "p.PartSize(%v) != b.PartSize(%v)", p.PartSize, b.PartSize)
   117  	}
   118  	return err
   119  }
   120  
   121  func EqualPart(p, b Part) (err error) {
   122  	if p.PartGUID != b.PartGUID {
   123  		err = errAppend(err, "p.PartGUID(%#x) != b.PartGUID(%#x)", p.PartGUID, b.PartGUID)
   124  	}
   125  	if p.UniqueGUID != b.UniqueGUID {
   126  		err = errAppend(err, "p.UniqueGUID(%#x) != b.UniqueGUID(%#x)", p.UniqueGUID, b.UniqueGUID)
   127  	}
   128  	if p.FirstLBA != b.FirstLBA {
   129  		err = errAppend(err, "p.FirstLBA(%#x) != b.FirstLBA(%#x)", p.FirstLBA, b.FirstLBA)
   130  	}
   131  	if p.LastLBA != b.LastLBA {
   132  		err = errAppend(err, "p.LastLBA(%#x) != b.LastLBA(%#x)", p.LastLBA, b.LastLBA)
   133  	}
   134  	// TODO: figure out what Attributes actually matter. We're not able to tell what differences
   135  	// matter and what differences don't.
   136  	if false && p.Attribute != b.Attribute {
   137  		err = errAppend(err, "p.Attribute(%#x) != b.Attribute(%#x)", p.Attribute, b.Attribute)
   138  	}
   139  	if p.Name != b.Name {
   140  		err = errAppend(err, "p.Name(%#x) != b.Name(%#x)", p.Name, b.Name)
   141  	}
   142  	return err
   143  }
   144  
   145  // EqualParts compares the Parts arrays from two GPTs
   146  // and returns an error if they differ.
   147  // If they length differs we just give up, since there's no way
   148  // to know which should have matched.
   149  // Otherwise, we do a 1:1 comparison.
   150  func EqualParts(p, b *GPT) (err error) {
   151  	if len(p.Parts) != len(b.Parts) {
   152  		return fmt.Errorf("Primary Number of partitions (%d) differs from Backup (%d)", len(p.Parts), len(b.Parts))
   153  	}
   154  	for i := range p.Parts {
   155  		if e := EqualPart(p.Parts[i], b.Parts[i]); e != nil {
   156  			err = errAppend(err, "Partition %d: %v", i, e)
   157  		}
   158  	}
   159  	return err
   160  }
   161  
   162  // Table reads a GPT table at the given offset.  It checks that
   163  // the Signature, Revision, HeaderSize, and MaxPart are reasonable. It
   164  // also verifies the header and partition table CRC32 values.
   165  func Table(r io.ReaderAt, off int64) (*GPT, error) {
   166  	which := "Primary"
   167  	if off != BlockSize {
   168  		which = "Backup"
   169  	}
   170  	var g = &GPT{}
   171  	if err := binary.Read(io.NewSectionReader(r, off, HeaderSize), binary.LittleEndian, &g.Header); err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	if g.Signature != Signature {
   176  		return nil, fmt.Errorf("%s GPT signature invalid (%x), needs to be %x", which, g.Signature, Signature)
   177  	}
   178  	if g.Revision != Revision {
   179  		return nil, fmt.Errorf("%s GPT revision (%x) is not supported value (%x)", which, g.Revision, Revision)
   180  	}
   181  	if g.HeaderSize != HeaderSize {
   182  		return nil, fmt.Errorf("%s GPT HeaderSize (%x) is not supported value (%x)", which, g.HeaderSize, HeaderSize)
   183  	}
   184  	if g.NPart > MaxNPart {
   185  		return nil, fmt.Errorf("%s GPT MaxNPart (%x) is above maximum of %x", which, g.NPart, MaxNPart)
   186  	}
   187  
   188  	// Read in all the partition data and check the hash.
   189  	// Since the partition hash is included in the header CRC,
   190  	// it's sensible to check it first.
   191  	s := int64(g.PartSize)
   192  	partBlocks := make([]byte, int64(g.NPart)*s)
   193  	n, err := r.ReadAt(partBlocks, int64(g.PartStart)*BlockSize)
   194  	if n != len(partBlocks) || err != nil {
   195  		return nil, fmt.Errorf("%s Reading partitions: Wanted %d bytes, got %d: %v", which, n, len(partBlocks), err)
   196  	}
   197  	if h := crc32.ChecksumIEEE(partBlocks); h != g.PartCRC {
   198  		return g, fmt.Errorf("%s Partition CRC: Header %v, computed checksum is %08x, header has %08x", which, g, h, g.PartCRC)
   199  	}
   200  
   201  	hdr := make([]byte, g.HeaderSize)
   202  	n, err = r.ReadAt(hdr, off)
   203  	if n != len(hdr) || err != nil {
   204  		return nil, fmt.Errorf("%s Reading Header: Wanted %d bytes, got %d: %v", which, n, len(hdr), err)
   205  	}
   206  	// Zap the checksum in the header to 0.
   207  	copy(hdr[16:], []byte{0, 0, 0, 0})
   208  	if h := crc32.ChecksumIEEE(hdr); h != g.CRC {
   209  		return g, fmt.Errorf("%s Header CRC: computed checksum is %08x, header has %08x", which, h, g.CRC)
   210  	}
   211  
   212  	// Now read in the partition table entries.
   213  	g.Parts = make([]Part, g.NPart)
   214  
   215  	for i := range g.Parts {
   216  		if err := binary.Read(io.NewSectionReader(r, int64(g.PartStart*BlockSize)+int64(i)*s, s), binary.LittleEndian, &g.Parts[i]); err != nil {
   217  			return nil, fmt.Errorf("%s GPT partition %d failed: %v", which, i, err)
   218  		}
   219  	}
   220  
   221  	return g, nil
   222  
   223  }
   224  
   225  // Write writes the GPT to w, both primary and backup. It generates the CRCs before writing.
   226  // It takes an io.Writer and assumes that you are correctly positioned in the output stream.
   227  // This means we must adjust partition numbers by subtracting one from them.
   228  func Write(w io.WriterAt, g *GPT) error {
   229  	// The maximum extent is NPart * PartSize
   230  	var h = make([]byte, uint64(g.NPart*g.PartSize))
   231  	s := int64(g.PartSize)
   232  	for i := int64(0); i < int64(g.NPart); i++ {
   233  		var b bytes.Buffer
   234  		if err := binary.Write(&b, binary.LittleEndian, &g.Parts[i]); err != nil {
   235  			return err
   236  		}
   237  		copy(h[i*s:], b.Bytes())
   238  	}
   239  
   240  	ps := int64(g.PartStart * BlockSize)
   241  	if _, err := w.WriteAt(h, ps); err != nil {
   242  		return fmt.Errorf("Writing %d bytes of partition table at %v: %v", len(h), ps, err)
   243  	}
   244  
   245  	g.PartCRC = crc32.ChecksumIEEE(h[:])
   246  	g.CRC = 0
   247  	var b bytes.Buffer
   248  	if err := binary.Write(&b, binary.LittleEndian, &g.Header); err != nil {
   249  		return err
   250  	}
   251  	h = make([]byte, g.HeaderSize)
   252  	copy(h, b.Bytes())
   253  	g.CRC = crc32.ChecksumIEEE(h[:])
   254  	b.Reset()
   255  	if err := binary.Write(&b, binary.LittleEndian, g.CRC); err != nil {
   256  		return err
   257  	}
   258  	copy(h[16:], b.Bytes())
   259  
   260  	_, err := w.WriteAt(h, int64(g.CurrentLBA*BlockSize))
   261  	return err
   262  }
   263  
   264  // New reads in the primary and backup GPT from a disk and returns a pointer to them.
   265  // There are some checks it can apply. It can return with a
   266  // one or more headers AND an error. Sorry. Experience with some real USB sticks
   267  // is showing that we need to return data even if there are some things wrong.
   268  func New(r io.ReaderAt) (*GPT, *GPT, error) {
   269  	g, err := Table(r, HeaderOff)
   270  	// If we can't read the table it's kinda hard to find the backup.
   271  	// Bit of a flaw in the design, eh?
   272  	if err != nil {
   273  		return nil, nil, err
   274  	}
   275  
   276  	b, err := Table(r, int64(g.BackupLBA*BlockSize))
   277  	if err != nil {
   278  		// you go to war with the army you have
   279  		return g, nil, err
   280  	}
   281  
   282  	if err := EqualHeader(g.Header, b.Header); err != nil {
   283  		return g, b, fmt.Errorf("Primary GPT and backup GPT Header differ: %v", err)
   284  	}
   285  
   286  	if g.CRC == b.CRC {
   287  		return g, b, fmt.Errorf("Primary (%v) and Backup (%v) Header CRC (%x) are the same and should differ", g.Header, b.Header, g.CRC)
   288  	}
   289  
   290  	if err := EqualParts(g, b); err != nil {
   291  		return b, g, err
   292  	}
   293  	return g, b, nil
   294  }