gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/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  }