github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/pkg/boot/grub/entry.go (about) 1 // Copyright 2020 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 package grub 6 7 import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "io" 12 "sort" 13 "strings" 14 ) 15 16 // GRUB block size. 17 const blockSize = 1024 18 19 // EnvFile is a GRUB environment file consisting of key-value pairs akin to the 20 // GRUB commands load_env and save_env. 21 type EnvFile struct { 22 Vars map[string]string 23 } 24 25 // NewEnvFile allocates a new env file. 26 func NewEnvFile() *EnvFile { 27 return &EnvFile{ 28 Vars: make(map[string]string), 29 } 30 } 31 32 // WriteTo writes key-value pairs to a file, padded to 1024 bytes, as save_env does. 33 func (env *EnvFile) WriteTo(w io.Writer) (int64, error) { 34 var b bytes.Buffer 35 b.WriteString("# GRUB Environment Block\n") 36 37 // Sort keys so order is deterministic. 38 keys := make([]string, 0, len(env.Vars)) 39 for k := range env.Vars { 40 keys = append(keys, k) 41 } 42 sort.Strings(keys) 43 44 for _, k := range keys { 45 if len(env.Vars[k]) > 0 { 46 b.WriteString(k) 47 b.WriteString("=") 48 b.WriteString(env.Vars[k]) 49 b.WriteString("\n") 50 } 51 } 52 length := b.Len() 53 54 // Fill up the file with # until 1024 bytes to make the file size a 55 // multiple of the block size. 56 remainder := blockSize - length%blockSize 57 for i := 0; i < remainder; i++ { 58 b.WriteByte('#') 59 } 60 61 return b.WriteTo(w) 62 } 63 64 // ParseEnvFile reads a key-value pair GRUB environment file. 65 // 66 // ParseEnvFile accepts incorrectly padded GRUB env files, as opposed to GRUB. 67 func ParseEnvFile(r io.Reader) (*EnvFile, error) { 68 s := bufio.NewScanner(r) 69 conf := NewEnvFile() 70 // Best lexer & parser in the world. 71 for s.Scan() { 72 if len(s.Text()) == 0 { 73 continue 74 } 75 // Comments. 76 if s.Text()[0] == '#' { 77 continue 78 } 79 80 tokens := strings.SplitN(s.Text(), "=", 2) 81 if len(tokens) != 2 { 82 return nil, fmt.Errorf("error parsing %q: must find = or # in each line", s.Text()) 83 } 84 key, value := tokens[0], tokens[1] 85 conf.Vars[key] = value 86 } 87 if err := s.Err(); err != nil { 88 return nil, err 89 } 90 return conf, nil 91 }