github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  	. "gopkg.in/check.v1"
    26  	"io"
    27  	"io/ioutil"
    28  	"path/filepath"
    29  	"testing"
    30  
    31  	"github.com/snapcore/snapd/bootloader/lkenv"
    32  )
    33  
    34  // Hook up check.v1 into the "go test" runner
    35  func Test(t *testing.T) { TestingT(t) }
    36  
    37  type lkenvTestSuite struct {
    38  	envPath    string
    39  	envPathbak string
    40  }
    41  
    42  var _ = Suite(&lkenvTestSuite{})
    43  
    44  func (l *lkenvTestSuite) SetUpTest(c *C) {
    45  	l.envPath = filepath.Join(c.MkDir(), "snapbootsel.bin")
    46  	l.envPathbak = l.envPath + "bak"
    47  }
    48  
    49  // unpack test data packed with gzip
    50  func unpackTestData(data []byte) (resData []byte, err error) {
    51  	b := bytes.NewBuffer(data)
    52  	var r io.Reader
    53  	r, err = gzip.NewReader(b)
    54  	if err != nil {
    55  		return
    56  	}
    57  	var env bytes.Buffer
    58  	_, err = env.ReadFrom(r)
    59  	if err != nil {
    60  		return
    61  	}
    62  	return env.Bytes(), nil
    63  }
    64  
    65  func (l *lkenvTestSuite) TestSet(c *C) {
    66  	env := lkenv.NewEnv(l.envPath)
    67  	c.Check(env, NotNil)
    68  
    69  	env.Set("snap_mode", "try")
    70  	c.Check(env.Get("snap_mode"), Equals, "try")
    71  }
    72  
    73  func (l *lkenvTestSuite) TestSave(c *C) {
    74  	buf := make([]byte, 4096)
    75  	err := ioutil.WriteFile(l.envPathbak, buf, 0644)
    76  	c.Assert(err, IsNil)
    77  	l.TestSaveNoBak(c)
    78  }
    79  
    80  func (l *lkenvTestSuite) TestCtoGoString(c *C) {
    81  	for _, t := range []struct {
    82  		input    []byte
    83  		expected string
    84  	}{
    85  		{[]byte{0, 0, 0, 0, 0}, ""},
    86  		{[]byte{'a', 0, 0, 0, 0}, "a"},
    87  		{[]byte{'a', 'b', 0, 0, 0}, "ab"},
    88  		{[]byte{'a', 'b', 'c', 0, 0}, "abc"},
    89  		{[]byte{'a', 'b', 'c', 'd', 0}, "abcd"},
    90  		// no trailing \0 - assume corrupted "" ?
    91  		{[]byte{'a', 'b', 'c', 'd', 'e'}, ""},
    92  		// first \0 is the cutof
    93  		{[]byte{'a', 'b', 0, 'z', 0}, "ab"},
    94  	} {
    95  		c.Check(lkenv.CToGoString(t.input), Equals, t.expected)
    96  	}
    97  
    98  }
    99  
   100  func (l *lkenvTestSuite) TestCopyStringHappy(c *C) {
   101  	for _, t := range []struct {
   102  		input    string
   103  		expected []byte
   104  	}{
   105  		// input up to the size of the buffer works
   106  		{"", []byte{0, 0, 0, 0, 0}},
   107  		{"a", []byte{'a', 0, 0, 0, 0}},
   108  		{"ab", []byte{'a', 'b', 0, 0, 0}},
   109  		{"abc", []byte{'a', 'b', 'c', 0, 0}},
   110  		{"abcd", []byte{'a', 'b', 'c', 'd', 0}},
   111  		// only what fit is copied
   112  		{"abcde", []byte{'a', 'b', 'c', 'd', 0}},
   113  		{"abcdef", []byte{'a', 'b', 'c', 'd', 0}},
   114  		// strange embedded stuff works
   115  		{"ab\000z", []byte{'a', 'b', 0, 'z', 0}},
   116  	} {
   117  		b := make([]byte, 5)
   118  		lkenv.CopyString(b, t.input)
   119  		c.Check(b, DeepEquals, t.expected)
   120  	}
   121  }
   122  
   123  func (l *lkenvTestSuite) TestCopyStringNoPanic(c *C) {
   124  	// too long, string should get concatenate
   125  	b := make([]byte, 5)
   126  	defer lkenv.CopyString(b, "12345")
   127  	c.Assert(recover(), IsNil)
   128  	defer lkenv.CopyString(b, "123456")
   129  	c.Assert(recover(), IsNil)
   130  }
   131  
   132  func (l *lkenvTestSuite) TestSaveNoBak(c *C) {
   133  	buf := make([]byte, 4096)
   134  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   135  	c.Assert(err, IsNil)
   136  
   137  	env := lkenv.NewEnv(l.envPath)
   138  	c.Check(env, NotNil)
   139  
   140  	env.Set("snap_mode", "trying")
   141  	env.Set("snap_kernel", "kernel-1")
   142  	env.Set("snap_try_kernel", "kernel-2")
   143  	env.Set("snap_core", "core-1")
   144  	env.Set("snap_try_core", "core-2")
   145  	env.Set("snap_gadget", "gadget-1")
   146  	env.Set("snap_try_gadget", "gadget-2")
   147  	env.Set("bootimg_file_name", "boot.img")
   148  
   149  	err = env.Save()
   150  	c.Assert(err, IsNil)
   151  
   152  	env2 := lkenv.NewEnv(l.envPath)
   153  	err = env2.Load()
   154  	c.Assert(err, IsNil)
   155  	c.Check(env2.Get("snap_mode"), Equals, "trying")
   156  	c.Check(env2.Get("snap_kernel"), Equals, "kernel-1")
   157  	c.Check(env2.Get("snap_try_kernel"), Equals, "kernel-2")
   158  	c.Check(env2.Get("snap_core"), Equals, "core-1")
   159  	c.Check(env2.Get("snap_try_core"), Equals, "core-2")
   160  	c.Check(env2.Get("snap_gadget"), Equals, "gadget-1")
   161  	c.Check(env2.Get("snap_try_gadget"), Equals, "gadget-2")
   162  	c.Check(env2.Get("bootimg_file_name"), Equals, "boot.img")
   163  }
   164  
   165  func (l *lkenvTestSuite) TestFailedCRC(c *C) {
   166  	buf := make([]byte, 4096)
   167  	err := ioutil.WriteFile(l.envPathbak, buf, 0644)
   168  	c.Assert(err, IsNil)
   169  	l.TestFailedCRCNoBak(c)
   170  }
   171  
   172  func (l *lkenvTestSuite) TestFailedCRCNoBak(c *C) {
   173  	buf := make([]byte, 4096)
   174  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   175  	c.Assert(err, IsNil)
   176  
   177  	env := lkenv.NewEnv(l.envPath)
   178  	c.Check(env, NotNil)
   179  
   180  	err = env.Load()
   181  	c.Assert(err, NotNil)
   182  }
   183  
   184  func (l *lkenvTestSuite) TestFailedCRCFallBack(c *C) {
   185  	buf := make([]byte, 4096)
   186  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   187  	c.Assert(err, IsNil)
   188  	err = ioutil.WriteFile(l.envPathbak, buf, 0644)
   189  	c.Assert(err, IsNil)
   190  
   191  	env := lkenv.NewEnv(l.envPath)
   192  	c.Check(env, NotNil)
   193  
   194  	env.Set("snap_mode", "trying")
   195  	env.Set("snap_kernel", "kernel-1")
   196  	env.Set("snap_try_kernel", "kernel-2")
   197  	err = env.Save()
   198  	c.Assert(err, IsNil)
   199  
   200  	// break main  env file
   201  	err = ioutil.WriteFile(l.envPath, buf, 0644)
   202  	c.Assert(err, IsNil)
   203  
   204  	env2 := lkenv.NewEnv(l.envPath)
   205  	err = env2.Load()
   206  	c.Assert(err, IsNil)
   207  	c.Check(env2.Get("snap_mode"), Equals, "trying")
   208  	c.Check(env2.Get("snap_kernel"), Equals, "kernel-1")
   209  	c.Check(env2.Get("snap_try_kernel"), Equals, "kernel-2")
   210  }
   211  
   212  func (l *lkenvTestSuite) TestGetBootPartition(c *C) {
   213  	buf := make([]byte, 4096)
   214  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   215  	c.Assert(err, IsNil)
   216  
   217  	env := lkenv.NewEnv(l.envPath)
   218  	c.Assert(err, IsNil)
   219  	env.ConfigureBootPartitions("boot_a", "boot_b")
   220  	// test no boot partition used
   221  	p, err := env.FindFreeBootPartition("kernel-1")
   222  	c.Check(p, Equals, "boot_a")
   223  	c.Assert(err, IsNil)
   224  	//  set kernel-2 to boot_a partition
   225  	err = env.SetBootPartition("boot_a", "kernel-1")
   226  	c.Assert(err, IsNil)
   227  	//  set kernel-2 to boot_a partition
   228  	err = env.SetBootPartition("boot_b", "kernel-2")
   229  	c.Assert(err, IsNil)
   230  
   231  	// 'boot_a' has 'kernel-1' revision
   232  	p, err = env.GetBootPartition("kernel-1")
   233  	c.Check(p, Equals, "boot_a")
   234  	c.Assert(err, IsNil)
   235  	// 'boot_b' has 'kernel-2' revision
   236  	p, err = env.GetBootPartition("kernel-2")
   237  	c.Check(p, Equals, "boot_b")
   238  	c.Assert(err, IsNil)
   239  }
   240  
   241  func (l *lkenvTestSuite) TestFindFree_Set_Free_BootPartition(c *C) {
   242  	buf := make([]byte, 4096)
   243  	err := ioutil.WriteFile(l.envPath, buf, 0644)
   244  	c.Assert(err, IsNil)
   245  
   246  	env := lkenv.NewEnv(l.envPath)
   247  	c.Assert(err, IsNil)
   248  	env.ConfigureBootPartitions("boot_a", "boot_b")
   249  	// test no boot partition used
   250  	p, err := env.FindFreeBootPartition("kernel-1")
   251  	c.Check(p, Equals, "boot_a")
   252  	c.Assert(err, IsNil)
   253  	//  set kernel-2 to boot_a partition
   254  	err = env.SetBootPartition("boot_a", "kernel-2")
   255  	c.Assert(err, IsNil)
   256  
   257  	env.Set("snap_kernel", "kernel-2")
   258  	// kernel-2 should now return first part, as it's already there
   259  	p, err = env.FindFreeBootPartition("kernel-2")
   260  	c.Check(p, Equals, "boot_a")
   261  	c.Assert(err, IsNil)
   262  	// test kernel-1 snapd, it should now offer second partition
   263  	p, err = env.FindFreeBootPartition("kernel-1")
   264  	c.Check(p, Equals, "boot_b")
   265  	c.Assert(err, IsNil)
   266  	err = env.SetBootPartition("boot_b", "kernel-1")
   267  	c.Assert(err, IsNil)
   268  	// set boot kernel-1
   269  	env.Set("snap_kernel", "kernel-1")
   270  	// now kernel-2 should not be protected and boot_a shoild be offered
   271  	p, err = env.FindFreeBootPartition("kernel-3")
   272  	c.Check(p, Equals, "boot_a")
   273  	c.Assert(err, IsNil)
   274  	err = env.SetBootPartition("boot_a", "kernel-3")
   275  	c.Assert(err, IsNil)
   276  	// remove kernel
   277  	used, err := env.FreeBootPartition("kernel-3")
   278  	c.Assert(err, IsNil)
   279  	c.Check(used, Equals, true)
   280  	// repeated use should return false and error
   281  	used, err = env.FreeBootPartition("kernel-3")
   282  	c.Assert(err, NotNil)
   283  	c.Check(used, Equals, false)
   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  }