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 }