|  | // Copyright 2021 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | // file2fuzz converts binary files, such as those used by go-fuzz, to the Go | 
|  | // fuzzing corpus format. | 
|  | // | 
|  | // Usage: | 
|  | // | 
|  | //	file2fuzz [-o output] [input...] | 
|  | // | 
|  | // The default behavior is to read input from stdin and write the converted | 
|  | // output to stdout. If any position arguments are provided stdin is ignored | 
|  | // and the arguments are assumed to be input files to convert. | 
|  | // | 
|  | // The -o flag provides an path to write output files to. If only one positional | 
|  | // argument is specified it may be a file path or an existing directory, if there are | 
|  | // multiple inputs specified it must be a directory. If a directory is provided | 
|  | // the name of the file will be the SHA-256 hash of its contents. | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "crypto/sha256" | 
|  | "errors" | 
|  | "flag" | 
|  | "fmt" | 
|  | "io" | 
|  | "io/ioutil" | 
|  | "log" | 
|  | "os" | 
|  | "path/filepath" | 
|  | ) | 
|  |  | 
|  | // encVersion1 is version 1 Go fuzzer corpus encoding. | 
|  | var encVersion1 = "go test fuzz v1" | 
|  |  | 
|  | func encodeByteSlice(b []byte) []byte { | 
|  | return []byte(fmt.Sprintf("%s\n[]byte(%q)", encVersion1, b)) | 
|  | } | 
|  |  | 
|  | func usage() { | 
|  | fmt.Fprintf(os.Stderr, "usage: file2fuzz [-o output] [input...]\nconverts files to Go fuzzer corpus format\n") | 
|  | fmt.Fprintf(os.Stderr, "\tinput: files to convert\n") | 
|  | fmt.Fprintf(os.Stderr, "\t-o: where to write converted file(s)\n") | 
|  | os.Exit(2) | 
|  | } | 
|  | func dirWriter(dir string) func([]byte) error { | 
|  | return func(b []byte) error { | 
|  | sum := fmt.Sprintf("%x", sha256.Sum256(b)) | 
|  | name := filepath.Join(dir, sum) | 
|  | if err := os.MkdirAll(dir, 0777); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := ioutil.WriteFile(name, b, 0666); err != nil { | 
|  | os.Remove(name) | 
|  | return err | 
|  | } | 
|  | return nil | 
|  | } | 
|  | } | 
|  |  | 
|  | func convert(inputArgs []string, outputArg string) error { | 
|  | var input []io.Reader | 
|  | if args := inputArgs; len(args) == 0 { | 
|  | input = []io.Reader{os.Stdin} | 
|  | } else { | 
|  | for _, a := range args { | 
|  | f, err := os.Open(a) | 
|  | if err != nil { | 
|  | return fmt.Errorf("unable to open %q: %s", a, err) | 
|  | } | 
|  | defer f.Close() | 
|  | if fi, err := f.Stat(); err != nil { | 
|  | return fmt.Errorf("unable to open %q: %s", a, err) | 
|  | } else if fi.IsDir() { | 
|  | return fmt.Errorf("%q is a directory, not a file", a) | 
|  | } | 
|  | input = append(input, f) | 
|  | } | 
|  | } | 
|  |  | 
|  | var output func([]byte) error | 
|  | if outputArg == "" { | 
|  | if len(inputArgs) > 1 { | 
|  | return errors.New("-o required with multiple input files") | 
|  | } | 
|  | output = func(b []byte) error { | 
|  | _, err := os.Stdout.Write(b) | 
|  | return err | 
|  | } | 
|  | } else { | 
|  | if len(inputArgs) > 1 { | 
|  | output = dirWriter(outputArg) | 
|  | } else { | 
|  | if fi, err := os.Stat(outputArg); err != nil && !os.IsNotExist(err) { | 
|  | return fmt.Errorf("unable to open %q for writing: %s", outputArg, err) | 
|  | } else if err == nil && fi.IsDir() { | 
|  | output = dirWriter(outputArg) | 
|  | } else { | 
|  | output = func(b []byte) error { | 
|  | return ioutil.WriteFile(outputArg, b, 0666) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | for _, f := range input { | 
|  | b, err := ioutil.ReadAll(f) | 
|  | if err != nil { | 
|  | return fmt.Errorf("unable to read input: %s", err) | 
|  | } | 
|  | if err := output(encodeByteSlice(b)); err != nil { | 
|  | return fmt.Errorf("unable to write output: %s", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func main() { | 
|  | log.SetFlags(0) | 
|  | log.SetPrefix("file2fuzz: ") | 
|  |  | 
|  | output := flag.String("o", "", "where to write converted file(s)") | 
|  | flag.Usage = usage | 
|  | flag.Parse() | 
|  |  | 
|  | if err := convert(flag.Args(), *output); err != nil { | 
|  | log.Fatal(err) | 
|  | } | 
|  | } |