github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/fmap/lib/fmap.go (about)

     1  // Copyright 2017 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  package fmap
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"encoding/binary"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  	"hash"
    15  	"io"
    16  	"io/ioutil"
    17  	"strconv"
    18  	"strings"
    19  )
    20  
    21  var signature = []byte("__FMAP__")
    22  
    23  // Flags which can be applied to Area.Flags.
    24  const (
    25  	FmapAreaStatic = 1 << iota
    26  	FmapAreaCompressed
    27  	FmapAreaReadOnly
    28  )
    29  
    30  // String wraps around byte array to give us more control over how strings are
    31  // serialized.
    32  type String struct {
    33  	Value [32]uint8
    34  }
    35  
    36  func (s *String) String() string {
    37  	return strings.TrimRight(string(s.Value[:]), "\x00")
    38  }
    39  
    40  // MarshalJSON implements json.Marshaler.
    41  func (s *String) MarshalJSON() ([]byte, error) {
    42  	return json.Marshal(s.String())
    43  }
    44  
    45  // UnmarshalJSON implements json.Unmarshaler.
    46  func (s *String) UnmarshalJSON(b []byte) error {
    47  	str, err := strconv.Unquote(string(b))
    48  	if err != nil {
    49  		return err
    50  	}
    51  	if len(str) > len(s.Value) {
    52  		return fmt.Errorf("String %#v is longer than 32 bytes", str)
    53  	}
    54  	copy(s.Value[:], []byte(str))
    55  	return nil
    56  }
    57  
    58  // FMap structure serializable using encoding.Binary.
    59  type FMap struct {
    60  	Header
    61  	Areas []Area
    62  }
    63  
    64  // Header describes the flash part.
    65  type Header struct {
    66  	Signature [8]uint8
    67  	VerMajor  uint8
    68  	VerMinor  uint8
    69  	Base      uint64
    70  	Size      uint32
    71  	Name      String
    72  	NAreas    uint16
    73  }
    74  
    75  // Area describes each area.
    76  type Area struct {
    77  	Offset uint32
    78  	Size   uint32
    79  	Name   String
    80  	Flags  uint16
    81  }
    82  
    83  // Metadata contains additional data not part of the FMap.
    84  type Metadata struct {
    85  	Start uint64
    86  }
    87  
    88  // FlagNames returns human readable representation of the flags.
    89  func FlagNames(flags uint16) string {
    90  	names := []string{}
    91  	m := []struct {
    92  		val  uint16
    93  		name string
    94  	}{
    95  		{FmapAreaStatic, "STATIC"},
    96  		{FmapAreaCompressed, "COMPRESSED"},
    97  		{FmapAreaReadOnly, "READ_ONLY"},
    98  	}
    99  	for _, v := range m {
   100  		if v.val&flags != 0 {
   101  			names = append(names, v.name)
   102  			flags -= v.val
   103  		}
   104  	}
   105  	// Write a hex value for unknown flags.
   106  	if flags != 0 || len(names) == 0 {
   107  		names = append(names, fmt.Sprintf("%#x", flags))
   108  	}
   109  	return strings.Join(names, "|")
   110  }
   111  
   112  func readField(r io.Reader, data interface{}) error {
   113  	// The endianness might depend on your machine or it might not.
   114  	if err := binary.Read(r, binary.LittleEndian, data); err != nil {
   115  		return errors.New("Unexpected EOF while parsing fmap")
   116  	}
   117  	return nil
   118  }
   119  
   120  // Read an FMap into the data structure.
   121  func Read(f io.Reader) (*FMap, *Metadata, error) {
   122  	// Read flash into memory.
   123  	// TODO: it is possible to parse fmap without reading entire file into memory
   124  	data, err := ioutil.ReadAll(f)
   125  	if err != nil {
   126  		return nil, nil, err
   127  	}
   128  
   129  	// Check for too many fmaps.
   130  	if bytes.Count(data, signature) >= 2 {
   131  		return nil, nil, errors.New("Found multiple signatures")
   132  	}
   133  
   134  	// Check for too few fmaps.
   135  	start := bytes.Index(data, signature)
   136  	if start == -1 {
   137  		return nil, nil, errors.New("Cannot find fmap signature")
   138  	}
   139  
   140  	// Reader anchored to the start of the fmap
   141  	r := bytes.NewReader(data[start:])
   142  
   143  	// Read fields.
   144  	var fmap FMap
   145  	if err := readField(r, &fmap.Header); err != nil {
   146  		return nil, nil, err
   147  	}
   148  	fmap.Areas = make([]Area, fmap.NAreas)
   149  	if err := readField(r, &fmap.Areas); err != nil {
   150  		return nil, nil, err
   151  	}
   152  
   153  	// Return useful metadata
   154  	fmapMetadata := Metadata{
   155  		Start: uint64(start),
   156  	}
   157  
   158  	return &fmap, &fmapMetadata, nil
   159  }
   160  
   161  // Write overwrites the fmap in the flash file.
   162  func Write(f io.WriteSeeker, fmap *FMap, m *Metadata) error {
   163  	if _, err := f.Seek(int64(m.Start), io.SeekStart); err != nil {
   164  		return err
   165  	}
   166  	if err := binary.Write(f, binary.LittleEndian, fmap.Header); err != nil {
   167  		return err
   168  	}
   169  	return binary.Write(f, binary.LittleEndian, fmap.Areas)
   170  }
   171  
   172  // ReadArea reads an area from the fmap as a binary stream.
   173  func (f *FMap) ReadArea(r io.ReadSeeker, i int) (io.Reader, error) {
   174  	if i < 0 || int(f.NAreas) <= i {
   175  		return nil, errors.New("Area index out of range")
   176  	}
   177  	if _, err := r.Seek(int64(f.Areas[i].Offset), io.SeekStart); err != nil {
   178  		return nil, err
   179  	}
   180  	return io.LimitReader(r, int64(f.Areas[i].Size)), nil
   181  }
   182  
   183  // Checksum performs a hash of the static areas.
   184  func (f *FMap) Checksum(r io.ReadSeeker, h hash.Hash) ([]byte, error) {
   185  	for i, v := range f.Areas {
   186  		if v.Flags&FmapAreaStatic == 0 {
   187  			continue
   188  		}
   189  		areaReader, err := f.ReadArea(r, i)
   190  		if err != nil {
   191  			return nil, err
   192  		}
   193  		_, err = bufio.NewReader(areaReader).WriteTo(h)
   194  		if err != nil {
   195  			return nil, err
   196  		}
   197  	}
   198  	return h.Sum([]byte{}), nil
   199  }