github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/bootloader/grubenv/grubenv.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package grubenv 21 22 import ( 23 "bytes" 24 "fmt" 25 "io/ioutil" 26 "os" 27 28 "github.com/snapcore/snapd/strutil" 29 ) 30 31 // FIXME: support for escaping (embedded \n in grubenv) missing 32 type Env struct { 33 env map[string]string 34 ordering []string 35 36 path string 37 } 38 39 func NewEnv(path string) *Env { 40 return &Env{ 41 env: make(map[string]string), 42 path: path, 43 } 44 } 45 46 func (g *Env) Get(name string) string { 47 return g.env[name] 48 } 49 50 func (g *Env) Set(key, value string) { 51 if !strutil.ListContains(g.ordering, key) { 52 g.ordering = append(g.ordering, key) 53 } 54 55 g.env[key] = value 56 } 57 58 func (g *Env) Load() error { 59 buf, err := ioutil.ReadFile(g.path) 60 if err != nil { 61 return err 62 } 63 if len(buf) != 1024 { 64 return fmt.Errorf("grubenv %q must be exactly 1024 byte, got %d", g.path, len(buf)) 65 } 66 if !bytes.HasPrefix(buf, []byte("# GRUB Environment Block\n")) { 67 return fmt.Errorf("cannot find grubenv header in %q", g.path) 68 } 69 rawEnv := bytes.Split(buf, []byte("\n")) 70 for _, env := range rawEnv[1:] { 71 l := bytes.SplitN(env, []byte("="), 2) 72 // be liberal in what you accept 73 if len(l) < 2 { 74 continue 75 } 76 k := string(l[0]) 77 v := string(l[1]) 78 g.env[k] = v 79 g.ordering = append(g.ordering, k) 80 } 81 82 return nil 83 } 84 85 func (g *Env) Save() error { 86 w := bytes.NewBuffer(nil) 87 w.Grow(1024) 88 89 fmt.Fprintf(w, "# GRUB Environment Block\n") 90 for _, k := range g.ordering { 91 if _, err := fmt.Fprintf(w, "%s=%s\n", k, g.env[k]); err != nil { 92 return err 93 } 94 } 95 if w.Len() > 1024 { 96 return fmt.Errorf("cannot write grubenv %q: bigger than 1024 bytes (%d)", g.path, w.Len()) 97 } 98 content := w.Bytes()[:w.Cap()] 99 for i := w.Len(); i < len(content); i++ { 100 content[i] = '#' 101 } 102 103 // write in place to avoid the file moving on disk 104 // (thats what grubenv is also doing) 105 f, err := os.Create(g.path) 106 if err != nil { 107 return err 108 } 109 if _, err := f.Write(content); err != nil { 110 return err 111 } 112 if err := f.Sync(); err != nil { 113 return err 114 } 115 116 return f.Close() 117 }