Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2392 from mvo5/feature/native-grubenv
partition: add support for native grubenv read/write and use it
- Loading branch information
Showing
5 changed files
with
258 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2014-2015 Canonical Ltd | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 3 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package grubenv | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
) | ||
|
||
// FIXME: support for escaping (embedded \n in grubenv) missing | ||
type Env struct { | ||
env map[string]string | ||
ordering []string | ||
|
||
path string | ||
} | ||
|
||
func NewEnv(path string) *Env { | ||
return &Env{ | ||
env: make(map[string]string), | ||
path: path, | ||
} | ||
} | ||
|
||
func (g *Env) Get(name string) string { | ||
return g.env[name] | ||
} | ||
|
||
func (g *Env) Set(key, value string) { | ||
var contains = func(needle string, haystack []string) bool { | ||
for _, k := range haystack { | ||
if k == key { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
if !contains(key, g.ordering) { | ||
g.ordering = append(g.ordering, key) | ||
} | ||
|
||
g.env[key] = value | ||
} | ||
|
||
func (g *Env) Load() error { | ||
buf, err := ioutil.ReadFile(g.path) | ||
if err != nil { | ||
return err | ||
} | ||
if len(buf) != 1024 { | ||
return fmt.Errorf("grubenv %q must be exactly 1024 byte, got %d", g.path, len(buf)) | ||
} | ||
if !bytes.HasPrefix(buf, []byte("# GRUB Environment Block\n")) { | ||
return fmt.Errorf("cannot find grubenv header in %q", g.path) | ||
} | ||
rawEnv := bytes.Split(buf, []byte("\n")) | ||
for _, env := range rawEnv[1:] { | ||
l := bytes.SplitN(env, []byte("="), 2) | ||
// be liberal in what you accept | ||
if len(l) < 2 { | ||
continue | ||
} | ||
k := string(l[0]) | ||
v := string(l[1]) | ||
g.env[k] = v | ||
g.ordering = append(g.ordering, k) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (g *Env) Save() error { | ||
w := bytes.NewBuffer(nil) | ||
w.Grow(1024) | ||
|
||
fmt.Fprintf(w, "# GRUB Environment Block\n") | ||
for _, k := range g.ordering { | ||
if _, err := fmt.Fprintf(w, "%s=%s\n", k, g.env[k]); err != nil { | ||
return err | ||
} | ||
} | ||
if w.Len() > 1024 { | ||
return fmt.Errorf("cannot write grubenv %q: bigger than 1024 bytes (%d)", g.path, w.Len()) | ||
} | ||
content := w.Bytes()[:w.Cap()] | ||
for i := w.Len(); i < len(content); i++ { | ||
content[i] = '#' | ||
} | ||
|
||
// write in place to avoid the file moving on disk | ||
// (thats what grubenv is also doing) | ||
f, err := os.Create(g.path) | ||
if err != nil { | ||
return err | ||
} | ||
if _, err := f.Write(content); err != nil { | ||
return err | ||
} | ||
if err := f.Sync(); err != nil { | ||
return err | ||
} | ||
|
||
return f.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// -*- Mode: Go; indent-tabs-mode: t -*- | ||
|
||
/* | ||
* Copyright (C) 2014-2015 Canonical Ltd | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 3 as | ||
* published by the Free Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
* | ||
*/ | ||
|
||
package grubenv_test | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"path/filepath" | ||
"testing" | ||
|
||
. "gopkg.in/check.v1" | ||
|
||
"github.com/snapcore/snapd/partition/grubenv" | ||
) | ||
|
||
// Hook up check.v1 into the "go test" runner | ||
func Test(t *testing.T) { TestingT(t) } | ||
|
||
type grubenvTestSuite struct { | ||
envPath string | ||
} | ||
|
||
var _ = Suite(&grubenvTestSuite{}) | ||
|
||
func (g *grubenvTestSuite) SetUpTest(c *C) { | ||
g.envPath = filepath.Join(c.MkDir(), "grubenv") | ||
} | ||
|
||
func (g *grubenvTestSuite) TestSet(c *C) { | ||
env := grubenv.NewEnv(g.envPath) | ||
c.Check(env, NotNil) | ||
|
||
env.Set("key", "value") | ||
c.Check(env.Get("key"), Equals, "value") | ||
} | ||
|
||
func (g *grubenvTestSuite) TestSave(c *C) { | ||
env := grubenv.NewEnv(g.envPath) | ||
c.Check(env, NotNil) | ||
|
||
env.Set("key1", "value1") | ||
env.Set("key2", "value2") | ||
env.Set("key3", "value3") | ||
env.Set("key4", "value4") | ||
env.Set("key5", "value5") | ||
env.Set("key6", "value6") | ||
env.Set("key7", "value7") | ||
// set "key1" again, ordering (position) does not change | ||
env.Set("key1", "value1") | ||
|
||
err := env.Save() | ||
c.Assert(err, IsNil) | ||
|
||
buf, err := ioutil.ReadFile(g.envPath) | ||
c.Assert(err, IsNil) | ||
c.Assert(buf, DeepEquals, []byte(`# GRUB Environment Block | ||
key1=value1 | ||
key2=value2 | ||
key3=value3 | ||
key4=value4 | ||
key5=value5 | ||
key6=value6 | ||
key7=value7 | ||
###################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################################`)) | ||
} | ||
|
||
func (g *grubenvTestSuite) TestSaveOverflow(c *C) { | ||
env := grubenv.NewEnv(g.envPath) | ||
c.Check(env, NotNil) | ||
|
||
for i := 0; i < 101; i++ { | ||
env.Set(fmt.Sprintf("key%d", i), "foo") | ||
} | ||
|
||
err := env.Save() | ||
c.Assert(err, ErrorMatches, `cannot write grubenv .*: bigger than 1024 bytes \(1026\)`) | ||
} |