github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/bootloader/lkenv/lkenv_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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 lkenv_test
    21  
    22  import (
    23  	"bytes"
    24  	"compress/gzip"
    25  	"io"
    26  	"io/ioutil"
    27  	"path/filepath"
    28  	"testing"
    29  
    30  	. "gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/boot"
    33  	"github.com/snapcore/snapd/bootloader/lkenv"
    34  )
    35  
    36  // Hook up check.v1 into the "go test" runner
    37  func Test(t *testing.T) { TestingT(t) }
    38  
    39  type lkenvTestSuite struct {
    40  	envPath    string
    41  	envPathbak string
    42  }
    43  
    44  var _ = Suite(&lkenvTestSuite{})
    45  
    46  func (l *lkenvTestSuite) SetUpTest(c *C) {
    47  	l.envPath = filepath.Join(c.MkDir(), "snapbootsel.bin")
    48  	l.envPathbak = l.envPath + "bak"
    49  }
    50  
    51  // unpack test data packed with gzip
    52  func unpackTestData(data []byte) (resData []byte, err error) {
    53  	b := bytes.NewBuffer(data)
    54  	var r io.Reader
    55  	r, err = gzip.NewReader(b)
    56  	if err != nil {
    57  		return
    58  	}
    59  	var env bytes.Buffer
    60  	_, err = env.ReadFrom(r)
    61  	if err != nil {
    62  		return
    63  	}
    64  	return env.Bytes(), nil
    65  }
    66  
    67  func (l *lkenvTestSuite) TestSet(c *C) {
    68  	env := lkenv.NewEnv(l.envPath)
    69  	c.Check(env, NotNil)
    70  
    71  	env.Set("snap_mode", boot.TryStatus)
    72  	c.Check(env.Get("snap_mode"), Equals, boot.TryStatus)
    73  }
    74  
    75  func (l *lkenvTestSuite) TestSave(c *C) {
    76  	buf := make([]byte, 4096)
    77  	err := ioutil.WriteFile(l.envPathbak, buf, 0644)
    78  	c.Assert(err, IsNil)
    79  	l.TestSaveNoBak(c)
    80  }
    81  
    82  func (l *lkenvTestSuite) TestCtoGoString(c *C) {
    83  	for _, t := range []struct {
    84  		input    []byte
    85  		expected string
    86  	}{
    87  		{[]byte{0, 0, 0, 0, 0}, ""},
    88  		{[]byte{'a', 0, 0, 0, 0}, "a"},
    89  		{[]byte{'a', 'b', 0, 0, 0}, "ab"},
    90  		{[]byte{'a', 'b', 'c', 0, 0}, "abc"},
    91  		{[]byte{'a', 'b', 'c', 'd', 0}, "abcd"},
    92  		// no trailing \0 - assume corrupted "" ?
    93  		{[]byte{'a', 'b', 'c', 'd', 'e'}, ""},
    94  		// first \0 is the cutof
    95  		{[]byte{'a', 'b', 0, 'z', 0}, "ab"},
    96  	} {
    97  		c.Check(lkenv.CToGoString(t.input), Equals, t.expected)
    98  	}
    99  
   100  }
   101  
   102  func (l *lkenvTestSuite) TestCopyStringHappy(c *C) {
   103  	for _, t := range []struct {
   104  		input    string
   105  		expected []byte
   106  	}{
   107  		// input up to the size of the buffer works
   108  		{"", []byte{0, 0, 0, 0, 0}},
   109  		{"a", []byte{'a', 0, 0, 0, 0}},
   110  		{"ab", []byte{'a', 'b', 0, 0, 0}},
   111  		{"abc", []byte{'a', 'b', 'c', 0, 0}},
   112  		{"abcd", []byte{'a', 'b', 'c', 'd', 0}},
   113  		// only what fit is copied
   114  		{"abcde", []byte{'a', 'b', 'c', 'd', 0}},
   115  		{"abcdef", []byte{'a', 'b', 'c', 'd', 0}},
   116  		// strange embedded stuff works
   117  		{"ab\000z", []byte{'a', 'b', 0, 'z', 0}},
   118  	} {
   119  		b := make([]byte, 5)
   120  		lkenv.CopyString(b, t.input)
   121  		c.Check(b, DeepEquals, t.expected)
   122  	}
   123  }
   124  
   125  func (l *lkenvTestSuite) TestCopyStringNoPanic(c *C) {
   126  	// too long, string should get concatenate
   127  	b := make([]byte, 5)
   128  	defer lkenv.CopyString(b, "12345")
   129  	c.Assert(recover(), IsNil)
   130  	defer lkenv.CopyString(b, "123456")
   131  	c.Assert(recover(), IsNil)
   132  }
   133  
   134  func (l *lkenvTestSuite) TestSaveNoBak(c *C) {
   135  	buf := make([]byte, 4096)
   136  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   137  	c.Assert(err, IsNil)
   138  
   139  	env := lkenv.NewEnv(l.envPath)
   140  	c.Check(env, NotNil)
   141  
   142  	env.Set("snap_mode", "trying")
   143  	env.Set("snap_kernel", "kernel-1")
   144  	env.Set("snap_try_kernel", "kernel-2")
   145  	env.Set("snap_core", "core-1")
   146  	env.Set("snap_try_core", "core-2")
   147  	env.Set("snap_gadget", "gadget-1")
   148  	env.Set("snap_try_gadget", "gadget-2")
   149  	env.Set("bootimg_file_name", "boot.img")
   150  
   151  	err = env.Save()
   152  	c.Assert(err, IsNil)
   153  
   154  	env2 := lkenv.NewEnv(l.envPath)
   155  	err = env2.Load()
   156  	c.Assert(err, IsNil)
   157  	c.Check(env2.Get("snap_mode"), Equals, "trying")
   158  	c.Check(env2.Get("snap_kernel"), Equals, "kernel-1")
   159  	c.Check(env2.Get("snap_try_kernel"), Equals, "kernel-2")
   160  	c.Check(env2.Get("snap_core"), Equals, "core-1")
   161  	c.Check(env2.Get("snap_try_core"), Equals, "core-2")
   162  	c.Check(env2.Get("snap_gadget"), Equals, "gadget-1")
   163  	c.Check(env2.Get("snap_try_gadget"), Equals, "gadget-2")
   164  	c.Check(env2.Get("bootimg_file_name"), Equals, "boot.img")
   165  }
   166  
   167  func (l *lkenvTestSuite) TestFailedCRC(c *C) {
   168  	buf := make([]byte, 4096)
   169  	err := ioutil.WriteFile(l.envPathbak, buf, 0644)
   170  	c.Assert(err, IsNil)
   171  	l.TestFailedCRCNoBak(c)
   172  }
   173  
   174  func (l *lkenvTestSuite) TestFailedCRCNoBak(c *C) {
   175  	buf := make([]byte, 4096)
   176  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   177  	c.Assert(err, IsNil)
   178  
   179  	env := lkenv.NewEnv(l.envPath)
   180  	c.Check(env, NotNil)
   181  
   182  	err = env.Load()
   183  	c.Assert(err, NotNil)
   184  }
   185  
   186  func (l *lkenvTestSuite) TestFailedCRCFallBack(c *C) {
   187  	buf := make([]byte, 4096)
   188  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   189  	c.Assert(err, IsNil)
   190  	err = ioutil.WriteFile(l.envPathbak, buf, 0644)
   191  	c.Assert(err, IsNil)
   192  
   193  	env := lkenv.NewEnv(l.envPath)
   194  	c.Check(env, NotNil)
   195  
   196  	env.Set("snap_mode", "trying")
   197  	env.Set("snap_kernel", "kernel-1")
   198  	env.Set("snap_try_kernel", "kernel-2")
   199  	err = env.Save()
   200  	c.Assert(err, IsNil)
   201  
   202  	// break main  env file
   203  	err = ioutil.WriteFile(l.envPath, buf, 0644)
   204  	c.Assert(err, IsNil)
   205  
   206  	env2 := lkenv.NewEnv(l.envPath)
   207  	err = env2.Load()
   208  	c.Assert(err, IsNil)
   209  	c.Check(env2.Get("snap_mode"), Equals, "trying")
   210  	c.Check(env2.Get("snap_kernel"), Equals, "kernel-1")
   211  	c.Check(env2.Get("snap_try_kernel"), Equals, "kernel-2")
   212  }
   213  
   214  func (l *lkenvTestSuite) TestGetBootPartition(c *C) {
   215  	buf := make([]byte, 4096)
   216  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   217  	c.Assert(err, IsNil)
   218  
   219  	env := lkenv.NewEnv(l.envPath)
   220  	c.Assert(err, IsNil)
   221  	env.ConfigureBootPartitions("boot_a", "boot_b")
   222  	// test no boot partition used
   223  	p, err := env.FindFreeBootPartition("kernel-1")
   224  	c.Check(p, Equals, "boot_a")
   225  	c.Assert(err, IsNil)
   226  	//  set kernel-2 to boot_a partition
   227  	err = env.SetBootPartition("boot_a", "kernel-1")
   228  	c.Assert(err, IsNil)
   229  	//  set kernel-2 to boot_a partition
   230  	err = env.SetBootPartition("boot_b", "kernel-2")
   231  	c.Assert(err, IsNil)
   232  
   233  	// 'boot_a' has 'kernel-1' revision
   234  	p, err = env.GetBootPartition("kernel-1")
   235  	c.Check(p, Equals, "boot_a")
   236  	c.Assert(err, IsNil)
   237  	// 'boot_b' has 'kernel-2' revision
   238  	p, err = env.GetBootPartition("kernel-2")
   239  	c.Check(p, Equals, "boot_b")
   240  	c.Assert(err, IsNil)
   241  }
   242  
   243  func (l *lkenvTestSuite) TestFindFree_Set_Free_BootPartition(c *C) {
   244  	buf := make([]byte, 4096)
   245  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   246  	c.Assert(err, IsNil)
   247  
   248  	env := lkenv.NewEnv(l.envPath)
   249  	c.Assert(err, IsNil)
   250  	env.ConfigureBootPartitions("boot_a", "boot_b")
   251  	// test no boot partition used
   252  	p, err := env.FindFreeBootPartition("kernel-1")
   253  	c.Check(p, Equals, "boot_a")
   254  	c.Assert(err, IsNil)
   255  	//  set kernel-2 to boot_a partition
   256  	err = env.SetBootPartition("boot_a", "kernel-2")
   257  	c.Assert(err, IsNil)
   258  
   259  	env.Set("snap_kernel", "kernel-2")
   260  	// kernel-2 should now return first part, as it's already there
   261  	p, err = env.FindFreeBootPartition("kernel-2")
   262  	c.Check(p, Equals, "boot_a")
   263  	c.Assert(err, IsNil)
   264  	// test kernel-1 snapd, it should now offer second partition
   265  	p, err = env.FindFreeBootPartition("kernel-1")
   266  	c.Check(p, Equals, "boot_b")
   267  	c.Assert(err, IsNil)
   268  	err = env.SetBootPartition("boot_b", "kernel-1")
   269  	c.Assert(err, IsNil)
   270  	// set boot kernel-1
   271  	env.Set("snap_kernel", "kernel-1")
   272  	// now kernel-2 should not be protected and boot_a shoild be offered
   273  	p, err = env.FindFreeBootPartition("kernel-3")
   274  	c.Check(p, Equals, "boot_a")
   275  	c.Assert(err, IsNil)
   276  	err = env.SetBootPartition("boot_a", "kernel-3")
   277  	c.Assert(err, IsNil)
   278  	// remove kernel
   279  	err = env.RemoveKernelRevisionFromBootPartition("kernel-3")
   280  	c.Assert(err, IsNil)
   281  	// repeated use should return false and error
   282  	err = env.RemoveKernelRevisionFromBootPartition("kernel-3")
   283  	c.Assert(err, NotNil)
   284  }
   285  
   286  func (l *lkenvTestSuite) TestZippedDataSample(c *C) {
   287  	// test data is generated with gadget build helper tool:
   288  	// $ parts/snap-boot-sel-env/build/lk-boot-env -w test.bin
   289  	//   --snap-mode="trying" --snap-kernel="kernel-1" --snap-try-kernel="kernel-2"
   290  	//   --snap-core="core-1" --snap-try-core="core-2" --reboot-reason=""
   291  	//   --boot-0-part="boot_a" --boot-1-part="boot_b" --boot-0-snap="kernel-1"
   292  	//   --boot-1-snap="kernel-3" --bootimg-file="boot.img"
   293  	// $ cat test.bin | gzip | xxd -i
   294  	gzipedData := []byte{
   295  		0x1f, 0x8b, 0x08, 0x00, 0x95, 0x88, 0x77, 0x5d, 0x00, 0x03, 0xed, 0xd7,
   296  		0xc1, 0x09, 0xc2, 0x40, 0x10, 0x05, 0xd0, 0xa4, 0x20, 0x05, 0x63, 0x07,
   297  		0x96, 0xa0, 0x05, 0x88, 0x91, 0x25, 0x04, 0x35, 0x0b, 0x6b, 0x2e, 0x1e,
   298  		0xac, 0xcb, 0xf6, 0xc4, 0x90, 0x1e, 0x06, 0xd9, 0xf7, 0x2a, 0xf8, 0xc3,
   299  		0x1f, 0x18, 0xe6, 0x74, 0x78, 0xa6, 0xb6, 0x69, 0x9b, 0xb9, 0xbc, 0xc6,
   300  		0x69, 0x68, 0xaa, 0x75, 0xcd, 0x25, 0x6d, 0x76, 0xd1, 0x29, 0xe2, 0x2c,
   301  		0xf3, 0x77, 0xd1, 0x29, 0xe2, 0xdc, 0x52, 0x99, 0xd2, 0xbd, 0xde, 0x0d,
   302  		0x58, 0xe7, 0xaf, 0x78, 0x03, 0x80, 0x5a, 0xf5, 0x39, 0xcf, 0xe7, 0x4b,
   303  		0x74, 0x8a, 0x38, 0xb5, 0xdf, 0xbf, 0xa5, 0xff, 0x3e, 0x3a, 0x45, 0x9c,
   304  		0xb5, 0xff, 0x7d, 0x74, 0x8e, 0x28, 0xbf, 0xfe, 0xb7, 0xe3, 0xa3, 0xe2,
   305  		0x0f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   306  		0xf8, 0x17, 0xc7, 0xf7, 0xa7, 0xfb, 0x02, 0x1c, 0xdf, 0x44, 0x21, 0x0c,
   307  		0x3a, 0x00, 0x00}
   308  
   309  	// uncompress test data to sample env file
   310  	rawData, err := unpackTestData(gzipedData)
   311  	c.Assert(err, IsNil)
   312  	err = ioutil.WriteFile(l.envPath, rawData, 0644)
   313  	c.Assert(err, IsNil)
   314  	err = ioutil.WriteFile(l.envPathbak, rawData, 0644)
   315  	c.Assert(err, IsNil)
   316  
   317  	env := lkenv.NewEnv(l.envPath)
   318  	c.Check(env, NotNil)
   319  	err = env.Load()
   320  	c.Assert(err, IsNil)
   321  	c.Check(env.Get("snap_mode"), Equals, "trying")
   322  	c.Check(env.Get("snap_kernel"), Equals, "kernel-1")
   323  	c.Check(env.Get("snap_try_kernel"), Equals, "kernel-2")
   324  	c.Check(env.Get("snap_core"), Equals, "core-1")
   325  	c.Check(env.Get("snap_try_core"), Equals, "core-2")
   326  	c.Check(env.Get("bootimg_file_name"), Equals, "boot.img")
   327  	c.Check(env.Get("reboot_reason"), Equals, "")
   328  	// first partition should be with label 'boot_a' and 'kernel-1' revision
   329  	p, err := env.GetBootPartition("kernel-1")
   330  	c.Check(p, Equals, "boot_a")
   331  	c.Assert(err, IsNil)
   332  	// test second boot partition is free with label "boot_b"
   333  	p, err = env.FindFreeBootPartition("kernel-2")
   334  	c.Check(p, Equals, "boot_b")
   335  	c.Assert(err, IsNil)
   336  }