1 // Copyright 2017 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package config 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io/ioutil" 10 "reflect" 11 "strings" 12 13 "github.com/google/syzkaller/pkg/osutil" 14 ) 15 16 func LoadFile(filename string, cfg interface{}) error { 17 if filename == "" { 18 return fmt.Errorf("no config file specified") 19 } 20 data, err := ioutil.ReadFile(filename) 21 if err != nil { 22 return fmt.Errorf("failed to read config file: %v", err) 23 } 24 return LoadData(data, cfg) 25 } 26 27 func LoadData(data []byte, cfg interface{}) error { 28 if err := checkUnknownFields(data, reflect.ValueOf(cfg).Type()); err != nil { 29 return err 30 } 31 if err := json.Unmarshal(data, cfg); err != nil { 32 return fmt.Errorf("failed to parse config file: %v", err) 33 } 34 return nil 35 } 36 37 func SaveFile(filename string, cfg interface{}) error { 38 data, err := SaveData(cfg) 39 if err != nil { 40 return err 41 } 42 return osutil.WriteFile(filename, data) 43 } 44 45 func SaveData(cfg interface{}) ([]byte, error) { 46 return json.MarshalIndent(cfg, "", "\t") 47 } 48 49 func checkUnknownFields(data []byte, typ reflect.Type) error { 50 if typ.Kind() != reflect.Ptr || typ.Elem().Kind() != reflect.Struct { 51 return fmt.Errorf("config type is not pointer to struct") 52 } 53 return checkUnknownFieldsRec(data, "", typ) 54 } 55 56 func checkUnknownFieldsRec(data []byte, prefix string, typ reflect.Type) error { 57 if typ.Kind() == reflect.Ptr { 58 typ = typ.Elem() 59 } 60 if typ.Kind() != reflect.Struct { 61 return fmt.Errorf("config type is not pointer to struct") 62 } 63 fields := make(map[string]reflect.Type) 64 for i := 0; i < typ.NumField(); i++ { 65 field := typ.Field(i) 66 tag := field.Tag.Get("json") 67 if tag == "-" { 68 continue 69 } 70 name := strings.ToLower(field.Name) 71 if tag != "" { 72 if tag != strings.ToLower(tag) { 73 return fmt.Errorf("json tag on '%v%v' should be lower-case", prefix, name) 74 } 75 name = tag 76 } 77 fields[name] = field.Type 78 } 79 f := make(map[string]interface{}) 80 if err := json.Unmarshal(data, &f); err != nil { 81 return fmt.Errorf("failed to parse config file: %v", err) 82 } 83 for k, v := range f { 84 field, ok := fields[strings.ToLower(k)] 85 if !ok { 86 return fmt.Errorf("unknown field '%v%v' in config", prefix, k) 87 } 88 if v != nil && field.Kind() == reflect.Slice && 89 (field.PkgPath() != "encoding/json" || field.Name() != "RawMessage") { 90 vv := reflect.ValueOf(v) 91 if vv.Type().Kind() != reflect.Slice { 92 return fmt.Errorf("bad json array type '%v%v'", prefix, k) 93 } 94 for i := 0; i < vv.Len(); i++ { 95 e := vv.Index(i).Interface() 96 prefix1 := fmt.Sprintf("%v%v[%v].", prefix, k, i) 97 if err := checkUnknownFieldsStruct(e, prefix1, field.Elem()); err != nil { 98 return err 99 } 100 } 101 } 102 if err := checkUnknownFieldsStruct(v, prefix+k+".", field); err != nil { 103 return err 104 } 105 } 106 return nil 107 } 108 109 func checkUnknownFieldsStruct(val interface{}, prefix string, typ reflect.Type) error { 110 if typ.Kind() == reflect.Ptr { 111 typ = typ.Elem() 112 } 113 if typ.Kind() != reflect.Struct { 114 return nil 115 } 116 if typ.PkgPath() == "time" && typ.Name() == "Time" { 117 return nil 118 } 119 inner, err := json.Marshal(val) 120 if err != nil { 121 return fmt.Errorf("failed to marshal inner struct %q: %v", prefix, err) 122 } 123 return checkUnknownFieldsRec(inner, prefix, typ) 124 } 125