golang.org/x/tools@v0.21.1-0.20240520172518-788d39e776b1/cmd/file2fuzz/main.go (about) 1 // Copyright 2021 The Go 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 // file2fuzz converts binary files, such as those used by go-fuzz, to the Go 6 // fuzzing corpus format. 7 // 8 // Usage: 9 // 10 // file2fuzz [-o output] [input...] 11 // 12 // The default behavior is to read input from stdin and write the converted 13 // output to stdout. If any position arguments are provided stdin is ignored 14 // and the arguments are assumed to be input files to convert. 15 // 16 // The -o flag provides an path to write output files to. If only one positional 17 // argument is specified it may be a file path or an existing directory, if there are 18 // multiple inputs specified it must be a directory. If a directory is provided 19 // the name of the file will be the SHA-256 hash of its contents. 20 package main 21 22 import ( 23 "crypto/sha256" 24 "errors" 25 "flag" 26 "fmt" 27 "io" 28 "log" 29 "os" 30 "path/filepath" 31 ) 32 33 // encVersion1 is version 1 Go fuzzer corpus encoding. 34 var encVersion1 = "go test fuzz v1" 35 36 func encodeByteSlice(b []byte) []byte { 37 return []byte(fmt.Sprintf("%s\n[]byte(%q)", encVersion1, b)) 38 } 39 40 func usage() { 41 fmt.Fprintf(os.Stderr, "usage: file2fuzz [-o output] [input...]\nconverts files to Go fuzzer corpus format\n") 42 fmt.Fprintf(os.Stderr, "\tinput: files to convert\n") 43 fmt.Fprintf(os.Stderr, "\t-o: where to write converted file(s)\n") 44 os.Exit(2) 45 } 46 func dirWriter(dir string) func([]byte) error { 47 return func(b []byte) error { 48 sum := fmt.Sprintf("%x", sha256.Sum256(b)) 49 name := filepath.Join(dir, sum) 50 if err := os.MkdirAll(dir, 0777); err != nil { 51 return err 52 } 53 if err := os.WriteFile(name, b, 0666); err != nil { 54 os.Remove(name) 55 return err 56 } 57 return nil 58 } 59 } 60 61 func convert(inputArgs []string, outputArg string) error { 62 var input []io.Reader 63 if args := inputArgs; len(args) == 0 { 64 input = []io.Reader{os.Stdin} 65 } else { 66 for _, a := range args { 67 f, err := os.Open(a) 68 if err != nil { 69 return fmt.Errorf("unable to open %q: %s", a, err) 70 } 71 defer f.Close() 72 if fi, err := f.Stat(); err != nil { 73 return fmt.Errorf("unable to open %q: %s", a, err) 74 } else if fi.IsDir() { 75 return fmt.Errorf("%q is a directory, not a file", a) 76 } 77 input = append(input, f) 78 } 79 } 80 81 var output func([]byte) error 82 if outputArg == "" { 83 if len(inputArgs) > 1 { 84 return errors.New("-o required with multiple input files") 85 } 86 output = func(b []byte) error { 87 _, err := os.Stdout.Write(b) 88 return err 89 } 90 } else { 91 if len(inputArgs) > 1 { 92 output = dirWriter(outputArg) 93 } else { 94 if fi, err := os.Stat(outputArg); err != nil && !os.IsNotExist(err) { 95 return fmt.Errorf("unable to open %q for writing: %s", outputArg, err) 96 } else if err == nil && fi.IsDir() { 97 output = dirWriter(outputArg) 98 } else { 99 output = func(b []byte) error { 100 return os.WriteFile(outputArg, b, 0666) 101 } 102 } 103 } 104 } 105 106 for _, f := range input { 107 b, err := io.ReadAll(f) 108 if err != nil { 109 return fmt.Errorf("unable to read input: %s", err) 110 } 111 if err := output(encodeByteSlice(b)); err != nil { 112 return fmt.Errorf("unable to write output: %s", err) 113 } 114 } 115 116 return nil 117 } 118 119 func main() { 120 log.SetFlags(0) 121 log.SetPrefix("file2fuzz: ") 122 123 output := flag.String("o", "", "where to write converted file(s)") 124 flag.Usage = usage 125 flag.Parse() 126 127 if err := convert(flag.Args(), *output); err != nil { 128 log.Fatal(err) 129 } 130 }