github.com/ratrocket/u-root@v0.0.0-20180201221235-1cf9f48ee2cf/cmds/fmap/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 // Fmap parses flash maps. 6 // 7 // Synopsis: 8 // fmap checksum [md5|sha1|sha256] FILE 9 // fmap extract i FILE 10 // fmap jget JSONFILE FILE 11 // fmap jput JSONFILE FILE 12 // fmap summary FILE 13 // fmap usage FILE 14 // fmap verify FILE 15 // 16 // Description: 17 // checksum: Print a checksum using the given hash function. 18 // extract: Print the i-th area of the flash. 19 // jget: Write json representation of the fmap to JSONFILE. 20 // jput: Replace current fmap with json representation in JSONFILE. 21 // summary: Print a human readable summary. 22 // usage: Print human readable usage stats. 23 // verify: Return 1 if the flash map is invalid. 24 // 25 // This implementation is based off of https://github.com/dhendrix/flashmap. 26 package main 27 28 import ( 29 "bytes" 30 "crypto/md5" 31 "crypto/sha1" 32 "crypto/sha256" 33 "encoding/json" 34 "errors" 35 "fmt" 36 "hash" 37 "io" 38 "io/ioutil" 39 "log" 40 "os" 41 "strconv" 42 "text/template" 43 44 fmap "github.com/u-root/u-root/cmds/fmap/lib" 45 ) 46 47 var cmds = map[string]struct { 48 nArgs int 49 parseFMap bool 50 f func(a cmdArgs) error 51 }{ 52 "checksum": {1, true, checksum}, 53 "extract": {1, true, extract}, 54 "jget": {1, true, jsonGet}, 55 "jput": {1, false, jsonPut}, 56 "summary": {0, true, summary}, 57 "usage": {0, true, usage}, 58 "verify": {0, true, verify}, 59 } 60 61 type cmdArgs struct { 62 args []string 63 f *fmap.FMap // optional 64 m *fmap.Metadata // optional 65 r io.ReadSeeker 66 } 67 68 var hashFuncs = map[string](func() hash.Hash){ 69 "md5": md5.New, 70 "sha1": sha1.New, 71 "sha256": sha256.New, 72 } 73 74 type jsonSchema struct { 75 FMap *fmap.FMap 76 Metadata *fmap.Metadata 77 } 78 79 // Print a checksum using the given hash function. 80 func checksum(a cmdArgs) error { 81 if _, ok := hashFuncs[a.args[0]]; !ok { 82 msg := "Not a valid hash function. Must be one of:" 83 for k := range hashFuncs { 84 msg += " " + k 85 } 86 return errors.New(msg) 87 } 88 89 checksum, err := a.f.Checksum(a.r, hashFuncs[a.args[0]]()) 90 if err != nil { 91 return err 92 } 93 fmt.Printf("%x\n", checksum) 94 return nil 95 } 96 97 // Print the i-th area of the flash. 98 func extract(a cmdArgs) error { 99 i, err := strconv.Atoi(a.args[0]) 100 if err != nil { 101 return err 102 } 103 areaReader, err := a.f.ReadArea(a.r, i) 104 if err != nil { 105 return err 106 } 107 _, err = io.Copy(os.Stdout, areaReader) 108 if err != nil { 109 return err 110 } 111 return nil 112 } 113 114 // Write json representation of the fmap to JSONFILE. 115 func jsonGet(a cmdArgs) error { 116 data, err := json.MarshalIndent(jsonSchema{a.f, a.m}, "", "\t") 117 if err != nil { 118 return err 119 } 120 data = append(data, byte('\n')) 121 if err := ioutil.WriteFile(a.args[0], data, 0666); err != nil { 122 return err 123 } 124 return nil 125 } 126 127 // Replace current fmap with json representation in JSONFILE. 128 func jsonPut(a cmdArgs) error { 129 r, err := os.OpenFile(os.Args[len(os.Args)-1], os.O_WRONLY, 0666) 130 if err != nil { 131 return err 132 } 133 defer r.Close() 134 135 data, err := ioutil.ReadFile(a.args[0]) 136 if err != nil { 137 return err 138 } 139 j := jsonSchema{} 140 if err := json.Unmarshal(data, &j); err != nil { 141 return err 142 } 143 return fmap.Write(r, j.FMap, j.Metadata) 144 } 145 146 // Print a human readable summary. 147 func summary(a cmdArgs) error { 148 const desc = `Fmap found at {{printf "%#x" .Metadata.Start}}: 149 Signature: {{printf "%s" .Signature}} 150 VerMajor: {{.VerMajor}} 151 VerMinor: {{.VerMinor}} 152 Base: {{printf "%#x" .Base}} 153 Size: {{printf "%#x" .Size}} 154 Name: {{.Name}} 155 NAreas: {{.NAreas}} 156 {{- range $i, $v := .Areas}} 157 Areas[{{$i}}]: 158 Offset: {{printf "%#x" $v.Offset}} 159 Size: {{printf "%#x" $v.Size}} 160 Name: {{$v.Name}} 161 Flags: {{printf "%#x" $v.Flags}} ({{FlagNames $v.Flags}}) 162 {{- end}} 163 ` 164 t := template.Must(template.New("desc"). 165 Funcs(template.FuncMap{"FlagNames": fmap.FlagNames}). 166 Parse(desc)) 167 // Combine the two structs to pass into template. 168 combined := struct { 169 *fmap.FMap 170 Metadata *fmap.Metadata 171 }{a.f, a.m} 172 if err := t.Execute(os.Stdout, combined); err != nil { 173 return err 174 } 175 return nil 176 } 177 178 // Print human readable usage stats. 179 func usage(a cmdArgs) error { 180 blockSize := 4 * 1024 181 rowLength := 32 182 183 buffer := make([]byte, blockSize) 184 fullBlock := bytes.Repeat([]byte{0xff}, blockSize) 185 zeroBlock := bytes.Repeat([]byte{0x00}, blockSize) 186 187 fmt.Println("Legend: '.' - full (0xff), '0' - zero (0x00), '#' - mixed") 188 189 if _, err := a.r.Seek(0, io.SeekStart); err != nil { 190 return err 191 } 192 193 var numBlocks, numFull, numZero int 194 loop: 195 for { 196 fmt.Printf("%#08x: ", numBlocks*blockSize) 197 for col := 0; col < rowLength; col++ { 198 // Read next block. 199 _, err := io.ReadFull(a.r, buffer) 200 if err == io.EOF { 201 fmt.Print("\n") 202 break loop 203 } else if err == io.ErrUnexpectedEOF { 204 fmt.Printf("\nWarning: flash is not a multiple of %d", len(buffer)) 205 break loop 206 } else if err != nil { 207 return err 208 } 209 numBlocks++ 210 211 // Analyze block. 212 if bytes.Equal(buffer, fullBlock) { 213 numFull++ 214 fmt.Print(".") 215 } else if bytes.Equal(buffer, zeroBlock) { 216 numZero++ 217 fmt.Print("0") 218 } else { 219 fmt.Print("#") 220 } 221 } 222 fmt.Print("\n") 223 } 224 225 // Print usage statistics. 226 print := func(name string, n int) { 227 fmt.Printf("%s %d (%.1f%%)\n", name, n, 228 float32(n)/float32(numBlocks)*100) 229 } 230 print("Blocks: ", numBlocks) 231 print("Full (0xff): ", numFull) 232 print("Empty (0x00):", numZero) 233 print("Mixed: ", numBlocks-numFull-numZero) 234 return nil 235 } 236 237 // Return 1 if the flash map is invalid. 238 func verify(a cmdArgs) error { 239 var err error 240 for i, area := range a.f.Areas { 241 if area.Offset+area.Size > a.f.Size { 242 err = errors.New("Invalid flash map") 243 log.Printf("Area %d is out of range", i) 244 } 245 } 246 return err 247 } 248 249 func printUsage() { 250 fmt.Printf("Usage: %s CMD [ARGS...] FILE\n", os.Args[0]) 251 fmt.Printf("CMD can be one of:\n") 252 for k := range cmds { 253 fmt.Printf("\t%s\n", k) 254 } 255 os.Exit(2) 256 } 257 258 func main() { 259 // Validate args. 260 if len(os.Args) <= 2 { 261 printUsage() 262 } 263 cmd, ok := cmds[os.Args[1]] 264 if !ok { 265 log.Printf("Invalid command %#v\n", os.Args[1]) 266 printUsage() 267 } 268 if len(os.Args) != cmd.nArgs+3 { 269 log.Printf("Expected %d arguments, got %d\n", cmd.nArgs+3, len(os.Args)) 270 printUsage() 271 } 272 273 // Args passed to the command. 274 a := cmdArgs{ 275 args: os.Args[2 : len(os.Args)-1], 276 } 277 278 // Open file and read fmap, but only for specific commands. 279 if cmd.parseFMap { 280 // Open file. 281 r, err := os.Open(os.Args[len(os.Args)-1]) 282 if err != nil { 283 log.Fatal(err) 284 } 285 a.r = r 286 defer r.Close() 287 288 // Parse fmap. 289 f, m, err := fmap.Read(r) 290 if err != nil { 291 log.Fatal(err) 292 } 293 a.f, a.m = f, m 294 } 295 296 // Execute command. 297 if err := cmd.f(a); err != nil { 298 log.Fatal(err) 299 } 300 }