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  }