github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/boot/boot_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2020 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 boot_test
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"testing"
    29  
    30  	. "gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/asserts"
    33  	"github.com/snapcore/snapd/boot"
    34  	"github.com/snapcore/snapd/boot/boottest"
    35  	"github.com/snapcore/snapd/bootloader"
    36  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    37  	"github.com/snapcore/snapd/dirs"
    38  	"github.com/snapcore/snapd/osutil"
    39  	"github.com/snapcore/snapd/secboot"
    40  	"github.com/snapcore/snapd/seed"
    41  	"github.com/snapcore/snapd/snap"
    42  	"github.com/snapcore/snapd/snap/snaptest"
    43  	"github.com/snapcore/snapd/testutil"
    44  	"github.com/snapcore/snapd/timings"
    45  )
    46  
    47  func TestBoot(t *testing.T) { TestingT(t) }
    48  
    49  type baseBootenvSuite struct {
    50  	testutil.BaseTest
    51  
    52  	rootdir     string
    53  	bootdir     string
    54  	cmdlineFile string
    55  }
    56  
    57  func (s *baseBootenvSuite) SetUpTest(c *C) {
    58  	s.BaseTest.SetUpTest(c)
    59  
    60  	s.rootdir = c.MkDir()
    61  	dirs.SetRootDir(s.rootdir)
    62  	s.AddCleanup(func() { dirs.SetRootDir("") })
    63  	restore := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
    64  	s.AddCleanup(restore)
    65  
    66  	s.bootdir = filepath.Join(s.rootdir, "boot")
    67  
    68  	s.cmdlineFile = filepath.Join(c.MkDir(), "cmdline")
    69  	restore = osutil.MockProcCmdline(s.cmdlineFile)
    70  	s.AddCleanup(restore)
    71  }
    72  
    73  func (s *baseBootenvSuite) forceBootloader(bloader bootloader.Bootloader) {
    74  	bootloader.Force(bloader)
    75  	s.AddCleanup(func() { bootloader.Force(nil) })
    76  }
    77  
    78  func (s *baseBootenvSuite) stampSealedKeys(c *C, rootdir string) {
    79  	stamp := filepath.Join(dirs.SnapFDEDirUnder(rootdir), "sealed-keys")
    80  	c.Assert(os.MkdirAll(filepath.Dir(stamp), 0755), IsNil)
    81  	err := ioutil.WriteFile(stamp, nil, 0644)
    82  	c.Assert(err, IsNil)
    83  }
    84  
    85  func (s *baseBootenvSuite) mockCmdline(c *C, cmdline string) {
    86  	c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil)
    87  }
    88  
    89  // mockAssetsCache mocks the listed assets in the boot assets cache by creating
    90  // an empty file for each.
    91  func mockAssetsCache(c *C, rootdir, bootloaderName string, cachedAssets []string) {
    92  	p := filepath.Join(dirs.SnapBootAssetsDirUnder(rootdir), bootloaderName)
    93  	err := os.MkdirAll(p, 0755)
    94  	c.Assert(err, IsNil)
    95  	for _, cachedAsset := range cachedAssets {
    96  		err = ioutil.WriteFile(filepath.Join(p, cachedAsset), nil, 0644)
    97  		c.Assert(err, IsNil)
    98  	}
    99  }
   100  
   101  type bootenvSuite struct {
   102  	baseBootenvSuite
   103  
   104  	bootloader *bootloadertest.MockBootloader
   105  }
   106  
   107  var _ = Suite(&bootenvSuite{})
   108  
   109  func (s *bootenvSuite) SetUpTest(c *C) {
   110  	s.baseBootenvSuite.SetUpTest(c)
   111  
   112  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   113  	s.forceBootloader(s.bootloader)
   114  }
   115  
   116  type baseBootenv20Suite struct {
   117  	baseBootenvSuite
   118  
   119  	kern1  snap.PlaceInfo
   120  	kern2  snap.PlaceInfo
   121  	ukern1 snap.PlaceInfo
   122  	ukern2 snap.PlaceInfo
   123  	base1  snap.PlaceInfo
   124  	base2  snap.PlaceInfo
   125  
   126  	normalDefaultState      *bootenv20Setup
   127  	normalTryingKernelState *bootenv20Setup
   128  }
   129  
   130  func (s *baseBootenv20Suite) SetUpTest(c *C) {
   131  	s.baseBootenvSuite.SetUpTest(c)
   132  
   133  	var err error
   134  	s.kern1, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
   135  	c.Assert(err, IsNil)
   136  	s.kern2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap")
   137  	c.Assert(err, IsNil)
   138  
   139  	s.ukern1, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_x1.snap")
   140  	c.Assert(err, IsNil)
   141  	s.ukern2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_x2.snap")
   142  	c.Assert(err, IsNil)
   143  
   144  	s.base1, err = snap.ParsePlaceInfoFromSnapFileName("core20_1.snap")
   145  	c.Assert(err, IsNil)
   146  	s.base2, err = snap.ParsePlaceInfoFromSnapFileName("core20_2.snap")
   147  	c.Assert(err, IsNil)
   148  
   149  	// default boot state for robustness tests, etc.
   150  	s.normalDefaultState = &bootenv20Setup{
   151  		modeenv: &boot.Modeenv{
   152  			// base is base1
   153  			Base: s.base1.Filename(),
   154  			// no try base
   155  			TryBase: "",
   156  			// base status is default
   157  			BaseStatus: boot.DefaultStatus,
   158  			// current kernels is just kern1
   159  			CurrentKernels: []string{s.kern1.Filename()},
   160  			// operating mode is run
   161  			Mode: "run",
   162  			// RecoverySystem is unset, as it should be during run mode
   163  			RecoverySystem: "",
   164  		},
   165  		// enabled kernel is kern1
   166  		kern: s.kern1,
   167  		// no try kernel enabled
   168  		tryKern: nil,
   169  		// kernel status is default
   170  		kernStatus: boot.DefaultStatus,
   171  	}
   172  
   173  	// state for after trying a new kernel for robustness tests, etc.
   174  	s.normalTryingKernelState = &bootenv20Setup{
   175  		modeenv: &boot.Modeenv{
   176  			// operating mode is run
   177  			Mode: "run",
   178  			// base is base1
   179  			Base: s.base1.Filename(),
   180  			// no try base
   181  			TryBase: "",
   182  			// base status is default
   183  			BaseStatus: boot.DefaultStatus,
   184  			// current kernels is kern1 + kern2
   185  			CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
   186  		},
   187  		// enabled kernel is kern1
   188  		kern: s.kern1,
   189  		// try kernel is kern2
   190  		tryKern: s.kern2,
   191  		// kernel status is trying
   192  		kernStatus: boot.TryingStatus,
   193  	}
   194  
   195  	s.mockCmdline(c, "snapd_recovery_mode=run")
   196  }
   197  
   198  type bootenv20Suite struct {
   199  	baseBootenv20Suite
   200  
   201  	bootloader *bootloadertest.MockExtractedRunKernelImageBootloader
   202  }
   203  
   204  type bootenv20EnvRefKernelSuite struct {
   205  	baseBootenv20Suite
   206  
   207  	bootloader *bootloadertest.MockBootloader
   208  }
   209  
   210  var defaultUC20BootEnv = map[string]string{"kernel_status": boot.DefaultStatus}
   211  
   212  var _ = Suite(&bootenv20Suite{})
   213  var _ = Suite(&bootenv20EnvRefKernelSuite{})
   214  
   215  func (s *bootenv20Suite) SetUpTest(c *C) {
   216  	s.baseBootenv20Suite.SetUpTest(c)
   217  
   218  	s.bootloader = bootloadertest.Mock("mock", c.MkDir()).WithExtractedRunKernelImage()
   219  	s.forceBootloader(s.bootloader)
   220  }
   221  
   222  func (s *bootenv20EnvRefKernelSuite) SetUpTest(c *C) {
   223  	s.baseBootenv20Suite.SetUpTest(c)
   224  
   225  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   226  	s.forceBootloader(s.bootloader)
   227  }
   228  
   229  type bootenv20Setup struct {
   230  	modeenv    *boot.Modeenv
   231  	kern       snap.PlaceInfo
   232  	tryKern    snap.PlaceInfo
   233  	kernStatus string
   234  }
   235  
   236  func setupUC20Bootenv(c *C, bl bootloader.Bootloader, opts *bootenv20Setup) (restore func()) {
   237  	var cleanups []func()
   238  
   239  	// write the modeenv
   240  	if opts.modeenv != nil {
   241  		c.Assert(opts.modeenv.WriteTo(""), IsNil)
   242  		// this isn't strictly necessary since the modeenv will be written to
   243  		// the test's private dir anyways, but it's nice to have so we can write
   244  		// multiple modeenvs from a single test and just call the restore
   245  		// function in between the parts of the test that use different modeenvs
   246  		r := func() {
   247  			defaultModeenv := &boot.Modeenv{Mode: "run"}
   248  			c.Assert(defaultModeenv.WriteTo(""), IsNil)
   249  		}
   250  		cleanups = append(cleanups, r)
   251  	}
   252  
   253  	// set the status
   254  	origEnv, err := bl.GetBootVars("kernel_status")
   255  	c.Assert(err, IsNil)
   256  
   257  	err = bl.SetBootVars(map[string]string{"kernel_status": opts.kernStatus})
   258  	c.Assert(err, IsNil)
   259  	cleanups = append(cleanups, func() {
   260  		err := bl.SetBootVars(origEnv)
   261  		c.Assert(err, IsNil)
   262  	})
   263  
   264  	// check what kind of real mock bootloader we have to use different methods
   265  	// to set the kernel snaps are if they're non-nil
   266  	switch vbl := bl.(type) {
   267  	case *bootloadertest.MockExtractedRunKernelImageBootloader:
   268  		// then we can use the advanced methods on it
   269  		if opts.kern != nil {
   270  			r := vbl.SetEnabledKernel(opts.kern)
   271  			cleanups = append(cleanups, r)
   272  		}
   273  
   274  		if opts.tryKern != nil {
   275  			r := vbl.SetEnabledTryKernel(opts.tryKern)
   276  			cleanups = append(cleanups, r)
   277  		}
   278  
   279  		// don't count any calls to SetBootVars made thus far
   280  		vbl.SetBootVarsCalls = 0
   281  
   282  	case *bootloadertest.MockBootloader:
   283  		// then we need to use the bootenv to set the current kernels
   284  		origEnv, err := vbl.GetBootVars("snap_kernel", "snap_try_kernel")
   285  		c.Assert(err, IsNil)
   286  		m := make(map[string]string, 2)
   287  		if opts.kern != nil {
   288  			m["snap_kernel"] = opts.kern.Filename()
   289  		} else {
   290  			m["snap_kernel"] = ""
   291  		}
   292  
   293  		if opts.tryKern != nil {
   294  			m["snap_try_kernel"] = opts.tryKern.Filename()
   295  		} else {
   296  			m["snap_try_kernel"] = ""
   297  		}
   298  
   299  		err = vbl.SetBootVars(m)
   300  		c.Assert(err, IsNil)
   301  
   302  		// don't count any calls to SetBootVars made thus far
   303  		vbl.SetBootVarsCalls = 0
   304  
   305  		cleanups = append(cleanups, func() {
   306  			err := bl.SetBootVars(origEnv)
   307  			c.Assert(err, IsNil)
   308  		})
   309  	default:
   310  		c.Fatalf("unsupported bootloader %T", bl)
   311  	}
   312  
   313  	return func() {
   314  		for _, r := range cleanups {
   315  			r()
   316  		}
   317  	}
   318  }
   319  
   320  func (s *bootenvSuite) TestInUseClassic(c *C) {
   321  	classicDev := boottest.MockDevice("")
   322  
   323  	// make bootloader.Find fail but shouldn't matter
   324  	bootloader.ForceError(errors.New("broken bootloader"))
   325  
   326  	inUse, err := boot.InUse(snap.TypeBase, classicDev)
   327  	c.Assert(err, IsNil)
   328  	c.Check(inUse("core18", snap.R(41)), Equals, false)
   329  }
   330  
   331  func (s *bootenvSuite) TestInUseIrrelevantTypes(c *C) {
   332  	coreDev := boottest.MockDevice("some-snap")
   333  
   334  	// make bootloader.Find fail but shouldn't matter
   335  	bootloader.ForceError(errors.New("broken bootloader"))
   336  
   337  	inUse, err := boot.InUse(snap.TypeGadget, coreDev)
   338  	c.Assert(err, IsNil)
   339  	c.Check(inUse("gadget", snap.R(41)), Equals, false)
   340  }
   341  
   342  func (s *bootenvSuite) TestInUse(c *C) {
   343  	coreDev := boottest.MockDevice("some-snap")
   344  
   345  	for _, t := range []struct {
   346  		bootVarKey   string
   347  		bootVarValue string
   348  
   349  		snapName string
   350  		snapRev  snap.Revision
   351  
   352  		inUse bool
   353  	}{
   354  		// in use
   355  		{"snap_kernel", "kernel_41.snap", "kernel", snap.R(41), true},
   356  		{"snap_try_kernel", "kernel_82.snap", "kernel", snap.R(82), true},
   357  		{"snap_core", "core_21.snap", "core", snap.R(21), true},
   358  		{"snap_try_core", "core_42.snap", "core", snap.R(42), true},
   359  		// not in use
   360  		{"snap_core", "core_111.snap", "core", snap.R(21), false},
   361  		{"snap_try_core", "core_111.snap", "core", snap.R(21), false},
   362  		{"snap_kernel", "kernel_111.snap", "kernel", snap.R(1), false},
   363  		{"snap_try_kernel", "kernel_111.snap", "kernel", snap.R(1), false},
   364  	} {
   365  		typ := snap.TypeBase
   366  		if t.snapName == "kernel" {
   367  			typ = snap.TypeKernel
   368  		}
   369  		s.bootloader.BootVars[t.bootVarKey] = t.bootVarValue
   370  		inUse, err := boot.InUse(typ, coreDev)
   371  		c.Assert(err, IsNil)
   372  		c.Assert(inUse(t.snapName, t.snapRev), Equals, t.inUse, Commentf("unexpected result: %s %s %v", t.snapName, t.snapRev, t.inUse))
   373  	}
   374  }
   375  
   376  func (s *bootenvSuite) TestInUseEphemeral(c *C) {
   377  	coreDev := boottest.MockDevice("some-snap@install")
   378  
   379  	// make bootloader.Find fail but shouldn't matter
   380  	bootloader.ForceError(errors.New("broken bootloader"))
   381  
   382  	inUse, err := boot.InUse(snap.TypeBase, coreDev)
   383  	c.Assert(err, IsNil)
   384  	c.Check(inUse("whatever", snap.R(0)), Equals, true)
   385  }
   386  
   387  func (s *bootenvSuite) TestInUseUnhappy(c *C) {
   388  	coreDev := boottest.MockDevice("some-snap")
   389  
   390  	// make GetVars fail
   391  	s.bootloader.GetErr = errors.New("zap")
   392  	_, err := boot.InUse(snap.TypeKernel, coreDev)
   393  	c.Check(err, ErrorMatches, `cannot get boot variables: zap`)
   394  
   395  	// make bootloader.Find fail
   396  	bootloader.ForceError(errors.New("broken bootloader"))
   397  	_, err = boot.InUse(snap.TypeKernel, coreDev)
   398  	c.Check(err, ErrorMatches, `cannot get boot settings: broken bootloader`)
   399  }
   400  
   401  func (s *bootenvSuite) TestCurrentBootNameAndRevision(c *C) {
   402  	coreDev := boottest.MockDevice("some-snap")
   403  
   404  	s.bootloader.BootVars["snap_core"] = "core_2.snap"
   405  	s.bootloader.BootVars["snap_kernel"] = "canonical-pc-linux_2.snap"
   406  
   407  	current, err := boot.GetCurrentBoot(snap.TypeOS, coreDev)
   408  	c.Check(err, IsNil)
   409  	c.Check(current.SnapName(), Equals, "core")
   410  	c.Check(current.SnapRevision(), Equals, snap.R(2))
   411  
   412  	current, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   413  	c.Check(err, IsNil)
   414  	c.Check(current.SnapName(), Equals, "canonical-pc-linux")
   415  	c.Check(current.SnapRevision(), Equals, snap.R(2))
   416  
   417  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
   418  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   419  	c.Check(err, Equals, boot.ErrBootNameAndRevisionNotReady)
   420  }
   421  
   422  func (s *bootenv20Suite) TestCurrentBoot20NameAndRevision(c *C) {
   423  	coreDev := boottest.MockUC20Device("", nil)
   424  	c.Assert(coreDev.HasModeenv(), Equals, true)
   425  
   426  	r := setupUC20Bootenv(
   427  		c,
   428  		s.bootloader,
   429  		s.normalDefaultState,
   430  	)
   431  	defer r()
   432  
   433  	current, err := boot.GetCurrentBoot(snap.TypeBase, coreDev)
   434  	c.Check(err, IsNil)
   435  	c.Check(current.SnapName(), Equals, s.base1.SnapName())
   436  	c.Check(current.SnapRevision(), Equals, snap.R(1))
   437  
   438  	current, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   439  	c.Check(err, IsNil)
   440  	c.Check(current.SnapName(), Equals, s.kern1.SnapName())
   441  	c.Check(current.SnapRevision(), Equals, snap.R(1))
   442  
   443  	s.bootloader.BootVars["kernel_status"] = boot.TryingStatus
   444  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   445  	c.Check(err, Equals, boot.ErrBootNameAndRevisionNotReady)
   446  }
   447  
   448  // only difference between this test and TestCurrentBoot20NameAndRevision is the
   449  // base bootloader which doesn't support ExtractedRunKernelImageBootloader.
   450  func (s *bootenv20EnvRefKernelSuite) TestCurrentBoot20NameAndRevision(c *C) {
   451  	coreDev := boottest.MockUC20Device("", nil)
   452  	c.Assert(coreDev.HasModeenv(), Equals, true)
   453  
   454  	r := setupUC20Bootenv(
   455  		c,
   456  		s.bootloader,
   457  		s.normalDefaultState,
   458  	)
   459  	defer r()
   460  
   461  	current, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   462  	c.Assert(err, IsNil)
   463  	c.Assert(current.SnapName(), Equals, s.kern1.SnapName())
   464  	c.Assert(current.SnapRevision(), Equals, snap.R(1))
   465  }
   466  
   467  func (s *bootenvSuite) TestCurrentBootNameAndRevisionUnhappy(c *C) {
   468  	coreDev := boottest.MockDevice("some-snap")
   469  
   470  	_, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   471  	c.Check(err, ErrorMatches, `cannot get name and revision of kernel \(snap_kernel\): boot variable unset`)
   472  
   473  	_, err = boot.GetCurrentBoot(snap.TypeOS, coreDev)
   474  	c.Check(err, ErrorMatches, `cannot get name and revision of boot base \(snap_core\): boot variable unset`)
   475  
   476  	_, err = boot.GetCurrentBoot(snap.TypeBase, coreDev)
   477  	c.Check(err, ErrorMatches, `cannot get name and revision of boot base \(snap_core\): boot variable unset`)
   478  
   479  	_, err = boot.GetCurrentBoot(snap.TypeApp, coreDev)
   480  	c.Check(err, ErrorMatches, `internal error: no boot state handling for snap type "app"`)
   481  
   482  	// sanity check
   483  	s.bootloader.BootVars["snap_kernel"] = "kernel_41.snap"
   484  	current, err := boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   485  	c.Check(err, IsNil)
   486  	c.Check(current.SnapName(), Equals, "kernel")
   487  	c.Check(current.SnapRevision(), Equals, snap.R(41))
   488  
   489  	// make GetVars fail
   490  	s.bootloader.GetErr = errors.New("zap")
   491  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   492  	c.Check(err, ErrorMatches, "cannot get boot variables: zap")
   493  	s.bootloader.GetErr = nil
   494  
   495  	// make bootloader.Find fail
   496  	bootloader.ForceError(errors.New("broken bootloader"))
   497  	_, err = boot.GetCurrentBoot(snap.TypeKernel, coreDev)
   498  	c.Check(err, ErrorMatches, "cannot get boot settings: broken bootloader")
   499  }
   500  
   501  func (s *bootenvSuite) TestParticipant(c *C) {
   502  	info := &snap.Info{}
   503  	info.RealName = "some-snap"
   504  
   505  	coreDev := boottest.MockDevice("some-snap")
   506  	classicDev := boottest.MockDevice("")
   507  
   508  	bp := boot.Participant(info, snap.TypeApp, coreDev)
   509  	c.Check(bp.IsTrivial(), Equals, true)
   510  
   511  	for _, typ := range []snap.Type{
   512  		snap.TypeKernel,
   513  		snap.TypeOS,
   514  		snap.TypeBase,
   515  	} {
   516  		bp = boot.Participant(info, typ, classicDev)
   517  		c.Check(bp.IsTrivial(), Equals, true)
   518  
   519  		bp = boot.Participant(info, typ, coreDev)
   520  		c.Check(bp.IsTrivial(), Equals, false)
   521  
   522  		c.Check(bp, DeepEquals, boot.NewCoreBootParticipant(info, typ, coreDev))
   523  	}
   524  }
   525  
   526  func (s *bootenvSuite) TestParticipantBaseWithModel(c *C) {
   527  	core := &snap.Info{SideInfo: snap.SideInfo{RealName: "core"}, SnapType: snap.TypeOS}
   528  	core18 := &snap.Info{SideInfo: snap.SideInfo{RealName: "core18"}, SnapType: snap.TypeBase}
   529  
   530  	type tableT struct {
   531  		with  *snap.Info
   532  		model string
   533  		nop   bool
   534  	}
   535  
   536  	table := []tableT{
   537  		{
   538  			with:  core,
   539  			model: "",
   540  			nop:   true,
   541  		}, {
   542  			with:  core,
   543  			model: "core",
   544  			nop:   false,
   545  		}, {
   546  			with:  core,
   547  			model: "core18",
   548  			nop:   true,
   549  		},
   550  		{
   551  			with:  core18,
   552  			model: "",
   553  			nop:   true,
   554  		},
   555  		{
   556  			with:  core18,
   557  			model: "core",
   558  			nop:   true,
   559  		},
   560  		{
   561  			with:  core18,
   562  			model: "core18",
   563  			nop:   false,
   564  		},
   565  		{
   566  			with:  core18,
   567  			model: "core18@install",
   568  			nop:   true,
   569  		},
   570  		{
   571  			with:  core,
   572  			model: "core@install",
   573  			nop:   true,
   574  		},
   575  	}
   576  
   577  	for i, t := range table {
   578  		dev := boottest.MockDevice(t.model)
   579  		bp := boot.Participant(t.with, t.with.Type(), dev)
   580  		c.Check(bp.IsTrivial(), Equals, t.nop, Commentf("%d", i))
   581  		if !t.nop {
   582  			c.Check(bp, DeepEquals, boot.NewCoreBootParticipant(t.with, t.with.Type(), dev))
   583  		}
   584  	}
   585  }
   586  
   587  func (s *bootenvSuite) TestKernelWithModel(c *C) {
   588  	info := &snap.Info{}
   589  	info.RealName = "kernel"
   590  
   591  	type tableT struct {
   592  		model string
   593  		nop   bool
   594  		krn   boot.BootKernel
   595  	}
   596  
   597  	table := []tableT{
   598  		{
   599  			model: "other-kernel",
   600  			nop:   true,
   601  			krn:   boot.Trivial{},
   602  		}, {
   603  			model: "kernel",
   604  			nop:   false,
   605  			krn:   boot.NewCoreKernel(info, boottest.MockDevice("kernel")),
   606  		}, {
   607  			model: "",
   608  			nop:   true,
   609  			krn:   boot.Trivial{},
   610  		}, {
   611  			model: "kernel@install",
   612  			nop:   true,
   613  			krn:   boot.Trivial{},
   614  		},
   615  	}
   616  
   617  	for _, t := range table {
   618  		dev := boottest.MockDevice(t.model)
   619  		krn := boot.Kernel(info, snap.TypeKernel, dev)
   620  		c.Check(krn.IsTrivial(), Equals, t.nop)
   621  		c.Check(krn, DeepEquals, t.krn)
   622  	}
   623  }
   624  
   625  func (s *bootenvSuite) TestMarkBootSuccessfulKernelStatusTryingNoTryKernelSnapCleansUp(c *C) {
   626  	coreDev := boottest.MockDevice("some-snap")
   627  
   628  	// set all the same vars as if we were doing trying, except don't set a try
   629  	// kernel
   630  
   631  	err := s.bootloader.SetBootVars(map[string]string{
   632  		"snap_kernel": "kernel_41.snap",
   633  		"snap_mode":   boot.TryingStatus,
   634  	})
   635  	c.Assert(err, IsNil)
   636  
   637  	// mark successful
   638  	err = boot.MarkBootSuccessful(coreDev)
   639  	c.Assert(err, IsNil)
   640  
   641  	// check that the bootloader variables were cleaned
   642  	expected := map[string]string{
   643  		"snap_mode":       boot.DefaultStatus,
   644  		"snap_kernel":     "kernel_41.snap",
   645  		"snap_try_kernel": "",
   646  	}
   647  	m, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   648  	c.Assert(err, IsNil)
   649  	c.Assert(m, DeepEquals, expected)
   650  
   651  	// do it again, verify it's still okay
   652  	err = boot.MarkBootSuccessful(coreDev)
   653  	c.Assert(err, IsNil)
   654  	m2, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   655  	c.Assert(err, IsNil)
   656  	c.Assert(m2, DeepEquals, expected)
   657  }
   658  
   659  func (s *bootenvSuite) TestMarkBootSuccessfulTryKernelKernelStatusDefaultCleansUp(c *C) {
   660  	coreDev := boottest.MockDevice("some-snap")
   661  
   662  	// set an errant snap_try_kernel
   663  	err := s.bootloader.SetBootVars(map[string]string{
   664  		"snap_kernel":     "kernel_41.snap",
   665  		"snap_try_kernel": "kernel_42.snap",
   666  		"snap_mode":       boot.DefaultStatus,
   667  	})
   668  	c.Assert(err, IsNil)
   669  
   670  	// mark successful
   671  	err = boot.MarkBootSuccessful(coreDev)
   672  	c.Assert(err, IsNil)
   673  
   674  	// check that the bootloader variables were cleaned
   675  	expected := map[string]string{
   676  		"snap_mode":       boot.DefaultStatus,
   677  		"snap_kernel":     "kernel_41.snap",
   678  		"snap_try_kernel": "",
   679  	}
   680  	m, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   681  	c.Assert(err, IsNil)
   682  	c.Assert(m, DeepEquals, expected)
   683  
   684  	// do it again, verify it's still okay
   685  	err = boot.MarkBootSuccessful(coreDev)
   686  	c.Assert(err, IsNil)
   687  	m2, err := s.bootloader.GetBootVars("snap_mode", "snap_try_kernel", "snap_kernel")
   688  	c.Assert(err, IsNil)
   689  	c.Assert(m2, DeepEquals, expected)
   690  }
   691  
   692  func (s *bootenv20Suite) TestCoreKernel20(c *C) {
   693  	coreDev := boottest.MockUC20Device("", nil)
   694  	c.Assert(coreDev.HasModeenv(), Equals, true)
   695  
   696  	r := setupUC20Bootenv(
   697  		c,
   698  		s.bootloader,
   699  		s.normalDefaultState,
   700  	)
   701  	defer r()
   702  
   703  	// get the boot kernel from our kernel snap
   704  	bootKern := boot.Kernel(s.kern1, snap.TypeKernel, coreDev)
   705  	// can't use FitsTypeOf with coreKernel here, cause that causes an import
   706  	// loop as boottest imports boot and coreKernel is unexported
   707  	c.Assert(bootKern.IsTrivial(), Equals, false)
   708  
   709  	// extract the kernel assets from the coreKernel
   710  	// the container here doesn't really matter since it's just being passed
   711  	// to the mock bootloader method anyways
   712  	kernelContainer := snaptest.MockContainer(c, nil)
   713  	err := bootKern.ExtractKernelAssets(kernelContainer)
   714  	c.Assert(err, IsNil)
   715  
   716  	// make sure that the bootloader was told to extract some assets
   717  	c.Assert(s.bootloader.ExtractKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{s.kern1})
   718  
   719  	// now remove the kernel assets and ensure that we get those calls
   720  	err = bootKern.RemoveKernelAssets()
   721  	c.Assert(err, IsNil)
   722  
   723  	// make sure that the bootloader was told to remove assets
   724  	c.Assert(s.bootloader.RemoveKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{s.kern1})
   725  }
   726  
   727  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnap(c *C) {
   728  	coreDev := boottest.MockUC20Device("", nil)
   729  	c.Assert(coreDev.HasModeenv(), Equals, true)
   730  
   731  	r := setupUC20Bootenv(
   732  		c,
   733  		s.bootloader,
   734  		s.normalDefaultState,
   735  	)
   736  	defer r()
   737  
   738  	// get the boot kernel participant from our kernel snap
   739  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
   740  
   741  	// make sure it's not a trivial boot participant
   742  	c.Assert(bootKern.IsTrivial(), Equals, false)
   743  
   744  	// make the kernel used on next boot
   745  	rebootRequired, err := bootKern.SetNextBoot()
   746  	c.Assert(err, IsNil)
   747  	c.Assert(rebootRequired, Equals, false)
   748  
   749  	// make sure that the bootloader was asked for the current kernel
   750  	_, nKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("Kernel")
   751  	c.Assert(nKernelCalls, Equals, 1)
   752  
   753  	// ensure that kernel_status is still empty
   754  	c.Assert(s.bootloader.BootVars["kernel_status"], Equals, boot.DefaultStatus)
   755  
   756  	// there was no attempt to enable a kernel
   757  	_, enableKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel")
   758  	c.Assert(enableKernelCalls, Equals, 0)
   759  
   760  	// the modeenv is still the same as well
   761  	m2, err := boot.ReadModeenv("")
   762  	c.Assert(err, IsNil)
   763  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
   764  
   765  	// finally we didn't call SetBootVars on the bootloader because nothing
   766  	// changed
   767  	c.Assert(s.bootloader.SetBootVarsCalls, Equals, 0)
   768  }
   769  
   770  func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextSameKernelSnap(c *C) {
   771  	coreDev := boottest.MockUC20Device("", nil)
   772  	c.Assert(coreDev.HasModeenv(), Equals, true)
   773  
   774  	r := setupUC20Bootenv(
   775  		c,
   776  		s.bootloader,
   777  		s.normalDefaultState,
   778  	)
   779  	defer r()
   780  
   781  	// get the boot kernel participant from our kernel snap
   782  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
   783  
   784  	// make sure it's not a trivial boot participant
   785  	c.Assert(bootKern.IsTrivial(), Equals, false)
   786  
   787  	// make the kernel used on next boot
   788  	rebootRequired, err := bootKern.SetNextBoot()
   789  	c.Assert(err, IsNil)
   790  	c.Assert(rebootRequired, Equals, false)
   791  
   792  	// ensure that bootenv is unchanged
   793  	m, err := s.bootloader.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
   794  	c.Assert(err, IsNil)
   795  	c.Assert(m, DeepEquals, map[string]string{
   796  		"kernel_status":   boot.DefaultStatus,
   797  		"snap_kernel":     s.kern1.Filename(),
   798  		"snap_try_kernel": "",
   799  	})
   800  
   801  	// the modeenv is still the same as well
   802  	m2, err := boot.ReadModeenv("")
   803  	c.Assert(err, IsNil)
   804  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
   805  
   806  	// finally we didn't call SetBootVars on the bootloader because nothing
   807  	// changed
   808  	c.Assert(s.bootloader.SetBootVarsCalls, Equals, 0)
   809  }
   810  
   811  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnap(c *C) {
   812  	coreDev := boottest.MockUC20Device("", nil)
   813  	c.Assert(coreDev.HasModeenv(), Equals, true)
   814  
   815  	r := setupUC20Bootenv(
   816  		c,
   817  		s.bootloader,
   818  		s.normalDefaultState,
   819  	)
   820  	defer r()
   821  
   822  	// get the boot kernel participant from our new kernel snap
   823  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
   824  	// make sure it's not a trivial boot participant
   825  	c.Assert(bootKern.IsTrivial(), Equals, false)
   826  
   827  	// make the kernel used on next boot
   828  	rebootRequired, err := bootKern.SetNextBoot()
   829  	c.Assert(err, IsNil)
   830  	c.Assert(rebootRequired, Equals, true)
   831  
   832  	// make sure that the bootloader was asked for the current kernel
   833  	_, nKernelCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("Kernel")
   834  	c.Assert(nKernelCalls, Equals, 1)
   835  
   836  	// ensure that kernel_status is now try
   837  	c.Assert(s.bootloader.BootVars["kernel_status"], Equals, boot.TryStatus)
   838  
   839  	// and we were asked to enable kernel2 as the try kernel
   840  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel")
   841  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
   842  
   843  	// and that the modeenv now has this kernel listed
   844  	m2, err := boot.ReadModeenv("")
   845  	c.Assert(err, IsNil)
   846  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
   847  }
   848  
   849  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewKernelSnapWithReseal(c *C) {
   850  	// checked by resealKeyToModeenv
   851  	s.stampSealedKeys(c, dirs.GlobalRootDir)
   852  
   853  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
   854  
   855  	data := []byte("foobar")
   856  	// SHA3-384
   857  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
   858  
   859  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
   860  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
   861  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
   862  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
   863  
   864  	// mock the files in cache
   865  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
   866  		"asset-" + dataHash,
   867  	})
   868  
   869  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
   870  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
   871  
   872  	tab.BootChainList = []bootloader.BootFile{
   873  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
   874  		// TODO:UC20: fix mocked trusted assets bootloader to actually
   875  		// geenerate kernel boot files
   876  		runKernelBf,
   877  	}
   878  
   879  	coreDev := boottest.MockUC20Device("", nil)
   880  	c.Assert(coreDev.HasModeenv(), Equals, true)
   881  
   882  	m := &boot.Modeenv{
   883  		Mode:           "run",
   884  		Base:           s.base1.Filename(),
   885  		CurrentKernels: []string{s.kern1.Filename()},
   886  		CurrentTrustedBootAssets: boot.BootAssetsMap{
   887  			"asset": {dataHash},
   888  		},
   889  	}
   890  
   891  	r := setupUC20Bootenv(
   892  		c,
   893  		tab.MockBootloader,
   894  		&bootenv20Setup{
   895  			modeenv:    m,
   896  			kern:       s.kern1,
   897  			kernStatus: boot.DefaultStatus,
   898  		},
   899  	)
   900  	defer r()
   901  
   902  	resealCalls := 0
   903  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
   904  		resealCalls++
   905  
   906  		c.Assert(params.ModelParams, HasLen, 1)
   907  		mp := params.ModelParams[0]
   908  		c.Check(mp.Model, DeepEquals, coreDev.Model())
   909  		for _, ch := range mp.EFILoadChains {
   910  			printChain(c, ch, "-")
   911  		}
   912  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
   913  			secboot.NewLoadChain(assetBf,
   914  				secboot.NewLoadChain(runKernelBf)),
   915  			secboot.NewLoadChain(assetBf,
   916  				// TODO:UC20: once mock trusted assets
   917  				// bootloader can generated boot files for the
   918  				// kernel this will use candidate kernel
   919  				secboot.NewLoadChain(runKernelBf)),
   920  		})
   921  		// actual paths are seen only here
   922  		c.Check(tab.BootChainKernelPath, DeepEquals, []string{
   923  			s.kern1.MountFile(),
   924  			s.kern2.MountFile(),
   925  		})
   926  		return nil
   927  	})
   928  	defer restore()
   929  
   930  	// get the boot kernel participant from our new kernel snap
   931  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
   932  	// make sure it's not a trivial boot participant
   933  	c.Assert(bootKern.IsTrivial(), Equals, false)
   934  
   935  	// make the kernel used on next boot
   936  	rebootRequired, err := bootKern.SetNextBoot()
   937  	c.Assert(err, IsNil)
   938  	c.Assert(rebootRequired, Equals, true)
   939  
   940  	// make sure the env was updated
   941  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
   942  	c.Assert(err, IsNil)
   943  	c.Assert(bvars, DeepEquals, map[string]string{
   944  		"kernel_status":   boot.TryStatus,
   945  		"snap_kernel":     s.kern1.Filename(),
   946  		"snap_try_kernel": s.kern2.Filename(),
   947  	})
   948  
   949  	// and that the modeenv now has this kernel listed
   950  	m2, err := boot.ReadModeenv("")
   951  	c.Assert(err, IsNil)
   952  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
   953  
   954  	c.Check(resealCalls, Equals, 1)
   955  }
   956  
   957  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewUnassertedKernelSnapWithReseal(c *C) {
   958  	// checked by resealKeyToModeenv
   959  	s.stampSealedKeys(c, dirs.GlobalRootDir)
   960  
   961  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
   962  
   963  	data := []byte("foobar")
   964  	// SHA3-384
   965  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
   966  
   967  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
   968  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
   969  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
   970  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
   971  
   972  	// mock the files in cache
   973  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
   974  		"asset-" + dataHash,
   975  	})
   976  
   977  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
   978  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
   979  
   980  	tab.BootChainList = []bootloader.BootFile{
   981  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
   982  		// TODO:UC20: fix mocked trusted assets bootloader to actually
   983  		// geenerate kernel boot files
   984  		runKernelBf,
   985  	}
   986  
   987  	uc20Model := boottest.MakeMockUC20Model()
   988  	coreDev := boottest.MockUC20Device("", uc20Model)
   989  	c.Assert(coreDev.HasModeenv(), Equals, true)
   990  
   991  	m := &boot.Modeenv{
   992  		Mode:           "run",
   993  		Base:           s.base1.Filename(),
   994  		CurrentKernels: []string{s.ukern1.Filename()},
   995  		CurrentTrustedBootAssets: boot.BootAssetsMap{
   996  			"asset": {dataHash},
   997  		},
   998  	}
   999  
  1000  	r := setupUC20Bootenv(
  1001  		c,
  1002  		tab.MockBootloader,
  1003  		&bootenv20Setup{
  1004  			modeenv:    m,
  1005  			kern:       s.ukern1,
  1006  			kernStatus: boot.DefaultStatus,
  1007  		},
  1008  	)
  1009  	defer r()
  1010  
  1011  	resealCalls := 0
  1012  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1013  		resealCalls++
  1014  
  1015  		c.Assert(params.ModelParams, HasLen, 1)
  1016  		mp := params.ModelParams[0]
  1017  		c.Check(mp.Model, DeepEquals, uc20Model)
  1018  		for _, ch := range mp.EFILoadChains {
  1019  			printChain(c, ch, "-")
  1020  		}
  1021  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  1022  			secboot.NewLoadChain(assetBf,
  1023  				secboot.NewLoadChain(runKernelBf)),
  1024  			secboot.NewLoadChain(assetBf,
  1025  				// TODO:UC20: once mock trusted assets
  1026  				// bootloader can generated boot files for the
  1027  				// kernel this will use candidate kernel
  1028  				secboot.NewLoadChain(runKernelBf)),
  1029  		})
  1030  		// actual paths are seen only here
  1031  		c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1032  			s.ukern1.MountFile(),
  1033  			s.ukern2.MountFile(),
  1034  		})
  1035  		return nil
  1036  	})
  1037  	defer restore()
  1038  
  1039  	// get the boot kernel participant from our new kernel snap
  1040  	bootKern := boot.Participant(s.ukern2, snap.TypeKernel, coreDev)
  1041  	// make sure it's not a trivial boot participant
  1042  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1043  
  1044  	// make the kernel used on next boot
  1045  	rebootRequired, err := bootKern.SetNextBoot()
  1046  	c.Assert(err, IsNil)
  1047  	c.Assert(rebootRequired, Equals, true)
  1048  
  1049  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1050  	c.Assert(err, IsNil)
  1051  	c.Assert(bvars, DeepEquals, map[string]string{
  1052  		"kernel_status":   boot.TryStatus,
  1053  		"snap_kernel":     s.ukern1.Filename(),
  1054  		"snap_try_kernel": s.ukern2.Filename(),
  1055  	})
  1056  
  1057  	// and that the modeenv now has this kernel listed
  1058  	m2, err := boot.ReadModeenv("")
  1059  	c.Assert(err, IsNil)
  1060  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename(), s.ukern2.Filename()})
  1061  
  1062  	c.Check(resealCalls, Equals, 1)
  1063  }
  1064  
  1065  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnapNoReseal(c *C) {
  1066  	// checked by resealKeyToModeenv
  1067  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1068  
  1069  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1070  
  1071  	data := []byte("foobar")
  1072  	// SHA3-384
  1073  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1074  
  1075  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1076  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1077  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1078  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1079  
  1080  	// mock the files in cache
  1081  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1082  		"asset-" + dataHash,
  1083  	})
  1084  
  1085  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1086  
  1087  	tab.BootChainList = []bootloader.BootFile{
  1088  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1089  		runKernelBf,
  1090  	}
  1091  
  1092  	uc20Model := boottest.MakeMockUC20Model()
  1093  	coreDev := boottest.MockUC20Device("", uc20Model)
  1094  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1095  
  1096  	m := &boot.Modeenv{
  1097  		Mode:           "run",
  1098  		Base:           s.base1.Filename(),
  1099  		CurrentKernels: []string{s.kern1.Filename()},
  1100  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1101  			"asset": {dataHash},
  1102  		},
  1103  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  1104  	}
  1105  
  1106  	r := setupUC20Bootenv(
  1107  		c,
  1108  		tab.MockBootloader,
  1109  		&bootenv20Setup{
  1110  			modeenv:    m,
  1111  			kern:       s.kern1,
  1112  			kernStatus: boot.DefaultStatus,
  1113  		},
  1114  	)
  1115  	defer r()
  1116  
  1117  	resealCalls := 0
  1118  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1119  		resealCalls++
  1120  		return fmt.Errorf("unexpected call")
  1121  	})
  1122  	defer restore()
  1123  
  1124  	// get the boot kernel participant from our kernel snap
  1125  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
  1126  	// make sure it's not a trivial boot participant
  1127  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1128  
  1129  	// write boot-chains for current state that will stay unchanged
  1130  	bootChains := []boot.BootChain{{
  1131  		BrandID:        "my-brand",
  1132  		Model:          "my-model-uc20",
  1133  		Grade:          "dangerous",
  1134  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1135  		AssetChain: []boot.BootAsset{
  1136  			{
  1137  				Role: bootloader.RoleRunMode,
  1138  				Name: "asset",
  1139  				Hashes: []string{
  1140  					"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1141  				},
  1142  			},
  1143  		},
  1144  		Kernel:         "pc-kernel",
  1145  		KernelRevision: "1",
  1146  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1147  	}}
  1148  	err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1149  	c.Assert(err, IsNil)
  1150  
  1151  	// make the kernel used on next boot
  1152  	rebootRequired, err := bootKern.SetNextBoot()
  1153  	c.Assert(err, IsNil)
  1154  	c.Assert(rebootRequired, Equals, false)
  1155  
  1156  	// make sure the env is as expected
  1157  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1158  	c.Assert(err, IsNil)
  1159  	c.Assert(bvars, DeepEquals, map[string]string{
  1160  		"kernel_status":   boot.DefaultStatus,
  1161  		"snap_kernel":     s.kern1.Filename(),
  1162  		"snap_try_kernel": "",
  1163  	})
  1164  
  1165  	// and that the modeenv now has the one kernel listed
  1166  	m2, err := boot.ReadModeenv("")
  1167  	c.Assert(err, IsNil)
  1168  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1169  
  1170  	// boot chains were built
  1171  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1172  		s.kern1.MountFile(),
  1173  	})
  1174  	// no actual reseal
  1175  	c.Check(resealCalls, Equals, 0)
  1176  }
  1177  
  1178  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameUnassertedKernelSnapNoReseal(c *C) {
  1179  	// checked by resealKeyToModeenv
  1180  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1181  
  1182  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1183  
  1184  	data := []byte("foobar")
  1185  	// SHA3-384
  1186  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1187  
  1188  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1189  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1190  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1191  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1192  
  1193  	// mock the files in cache
  1194  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1195  		"asset-" + dataHash,
  1196  	})
  1197  
  1198  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1199  
  1200  	tab.BootChainList = []bootloader.BootFile{
  1201  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1202  		runKernelBf,
  1203  	}
  1204  
  1205  	uc20Model := boottest.MakeMockUC20Model()
  1206  	coreDev := boottest.MockUC20Device("", uc20Model)
  1207  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1208  
  1209  	m := &boot.Modeenv{
  1210  		Mode:           "run",
  1211  		Base:           s.base1.Filename(),
  1212  		CurrentKernels: []string{s.ukern1.Filename()},
  1213  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1214  			"asset": {dataHash},
  1215  		},
  1216  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  1217  	}
  1218  
  1219  	r := setupUC20Bootenv(
  1220  		c,
  1221  		tab.MockBootloader,
  1222  		&bootenv20Setup{
  1223  			modeenv:    m,
  1224  			kern:       s.ukern1,
  1225  			kernStatus: boot.DefaultStatus,
  1226  		},
  1227  	)
  1228  	defer r()
  1229  
  1230  	resealCalls := 0
  1231  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1232  		resealCalls++
  1233  		return fmt.Errorf("unexpected call")
  1234  	})
  1235  	defer restore()
  1236  
  1237  	// get the boot kernel participant from our kernel snap
  1238  	bootKern := boot.Participant(s.ukern1, snap.TypeKernel, coreDev)
  1239  	// make sure it's not a trivial boot participant
  1240  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1241  
  1242  	// write boot-chains for current state that will stay unchanged
  1243  	bootChains := []boot.BootChain{{
  1244  		BrandID:        "my-brand",
  1245  		Model:          "my-model-uc20",
  1246  		Grade:          "dangerous",
  1247  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1248  		AssetChain: []boot.BootAsset{
  1249  			{
  1250  				Role: bootloader.RoleRunMode,
  1251  				Name: "asset",
  1252  				Hashes: []string{
  1253  					"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1254  				},
  1255  			},
  1256  		},
  1257  		Kernel:         "pc-kernel",
  1258  		KernelRevision: "",
  1259  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1260  	}}
  1261  	err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1262  	c.Assert(err, IsNil)
  1263  
  1264  	// make the kernel used on next boot
  1265  	rebootRequired, err := bootKern.SetNextBoot()
  1266  	c.Assert(err, IsNil)
  1267  	c.Assert(rebootRequired, Equals, false)
  1268  
  1269  	// make sure the env is as expected
  1270  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1271  	c.Assert(err, IsNil)
  1272  	c.Assert(bvars, DeepEquals, map[string]string{
  1273  		"kernel_status":   boot.DefaultStatus,
  1274  		"snap_kernel":     s.ukern1.Filename(),
  1275  		"snap_try_kernel": "",
  1276  	})
  1277  
  1278  	// and that the modeenv now has the one kernel listed
  1279  	m2, err := boot.ReadModeenv("")
  1280  	c.Assert(err, IsNil)
  1281  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename()})
  1282  
  1283  	// boot chains were built
  1284  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1285  		s.ukern1.MountFile(),
  1286  	})
  1287  	// no actual reseal
  1288  	c.Check(resealCalls, Equals, 0)
  1289  }
  1290  
  1291  func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextNewKernelSnap(c *C) {
  1292  	coreDev := boottest.MockUC20Device("", nil)
  1293  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1294  
  1295  	r := setupUC20Bootenv(
  1296  		c,
  1297  		s.bootloader,
  1298  		s.normalDefaultState,
  1299  	)
  1300  	defer r()
  1301  
  1302  	// get the boot kernel participant from our new kernel snap
  1303  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
  1304  	// make sure it's not a trivial boot participant
  1305  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1306  
  1307  	// make the kernel used on next boot
  1308  	rebootRequired, err := bootKern.SetNextBoot()
  1309  	c.Assert(err, IsNil)
  1310  	c.Assert(rebootRequired, Equals, true)
  1311  
  1312  	// make sure the env was updated
  1313  	m := s.bootloader.BootVars
  1314  	c.Assert(m, DeepEquals, map[string]string{
  1315  		"kernel_status":   boot.TryStatus,
  1316  		"snap_kernel":     s.kern1.Filename(),
  1317  		"snap_try_kernel": s.kern2.Filename(),
  1318  	})
  1319  
  1320  	// and that the modeenv now has this kernel listed
  1321  	m2, err := boot.ReadModeenv("")
  1322  	c.Assert(err, IsNil)
  1323  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
  1324  }
  1325  
  1326  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) {
  1327  	coreDev := boottest.MockUC20Device("", nil)
  1328  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1329  
  1330  	// set all the same vars as if we were doing trying, except don't set a try
  1331  	// kernel
  1332  	r := setupUC20Bootenv(
  1333  		c,
  1334  		s.bootloader,
  1335  		&bootenv20Setup{
  1336  			modeenv: &boot.Modeenv{
  1337  				Mode:           "run",
  1338  				Base:           s.base1.Filename(),
  1339  				CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1340  			},
  1341  			kern: s.kern1,
  1342  			// no try-kernel
  1343  			kernStatus: boot.TryingStatus,
  1344  		},
  1345  	)
  1346  	defer r()
  1347  
  1348  	// mark successful
  1349  	err := boot.MarkBootSuccessful(coreDev)
  1350  	c.Assert(err, IsNil)
  1351  
  1352  	// check that the bootloader variable was cleaned
  1353  	expected := map[string]string{"kernel_status": boot.DefaultStatus}
  1354  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1355  
  1356  	// check that MarkBootSuccessful didn't enable a kernel (since there was no
  1357  	// try kernel)
  1358  	_, nEnableCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1359  	c.Assert(nEnableCalls, Equals, 0)
  1360  
  1361  	// we will always end up disabling a try-kernel though as cleanup
  1362  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1363  	c.Assert(nDisableTryCalls, Equals, 1)
  1364  
  1365  	// do it again, verify it's still okay
  1366  	err = boot.MarkBootSuccessful(coreDev)
  1367  	c.Assert(err, IsNil)
  1368  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1369  
  1370  	// no new enabled kernels
  1371  	_, nEnableCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1372  	c.Assert(nEnableCalls, Equals, 0)
  1373  
  1374  	// again we will try to cleanup any leftover try-kernels
  1375  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1376  	c.Assert(nDisableTryCalls, Equals, 2)
  1377  
  1378  	// check that the modeenv re-wrote the CurrentKernels
  1379  	m2, err := boot.ReadModeenv("")
  1380  	c.Assert(err, IsNil)
  1381  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1382  }
  1383  
  1384  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) {
  1385  	coreDev := boottest.MockUC20Device("", nil)
  1386  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1387  
  1388  	// set all the same vars as if we were doing trying, except don't set a try
  1389  	// kernel
  1390  	r := setupUC20Bootenv(
  1391  		c,
  1392  		s.bootloader,
  1393  		&bootenv20Setup{
  1394  			modeenv: &boot.Modeenv{
  1395  				Mode:           "run",
  1396  				Base:           s.base1.Filename(),
  1397  				CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1398  			},
  1399  			kern: s.kern1,
  1400  			// no try-kernel
  1401  			kernStatus: boot.TryingStatus,
  1402  		},
  1403  	)
  1404  	defer r()
  1405  
  1406  	// mark successful
  1407  	err := boot.MarkBootSuccessful(coreDev)
  1408  	c.Assert(err, IsNil)
  1409  
  1410  	// make sure the env was updated
  1411  	expected := map[string]string{
  1412  		"kernel_status":   boot.DefaultStatus,
  1413  		"snap_kernel":     s.kern1.Filename(),
  1414  		"snap_try_kernel": "",
  1415  	}
  1416  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1417  
  1418  	// do it again, verify it's still okay
  1419  	err = boot.MarkBootSuccessful(coreDev)
  1420  	c.Assert(err, IsNil)
  1421  
  1422  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1423  
  1424  	// check that the modeenv re-wrote the CurrentKernels
  1425  	m2, err := boot.ReadModeenv("")
  1426  	c.Assert(err, IsNil)
  1427  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1428  }
  1429  
  1430  func (s *bootenv20Suite) TestMarkBootSuccessful20BaseStatusTryingNoTryBaseSnapCleansUp(c *C) {
  1431  	m := &boot.Modeenv{
  1432  		Mode: "run",
  1433  		Base: s.base1.Filename(),
  1434  		// no TryBase set
  1435  		BaseStatus: boot.TryingStatus,
  1436  	}
  1437  	r := setupUC20Bootenv(
  1438  		c,
  1439  		s.bootloader,
  1440  		&bootenv20Setup{
  1441  			modeenv: m,
  1442  			// no kernel setup necessary
  1443  		},
  1444  	)
  1445  	defer r()
  1446  
  1447  	coreDev := boottest.MockUC20Device("", nil)
  1448  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1449  
  1450  	// mark successful
  1451  	err := boot.MarkBootSuccessful(coreDev)
  1452  	c.Assert(err, IsNil)
  1453  
  1454  	// check that the modeenv base_status was re-written to default
  1455  	m2, err := boot.ReadModeenv("")
  1456  	c.Assert(err, IsNil)
  1457  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1458  	c.Assert(m2.Base, Equals, m.Base)
  1459  	c.Assert(m2.TryBase, Equals, m.TryBase)
  1460  
  1461  	// do it again, verify it's still okay
  1462  	err = boot.MarkBootSuccessful(coreDev)
  1463  	c.Assert(err, IsNil)
  1464  
  1465  	m3, err := boot.ReadModeenv("")
  1466  	c.Assert(err, IsNil)
  1467  	c.Assert(m3.BaseStatus, Equals, boot.DefaultStatus)
  1468  	c.Assert(m3.Base, Equals, m.Base)
  1469  	c.Assert(m3.TryBase, Equals, m.TryBase)
  1470  }
  1471  
  1472  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameBaseSnap(c *C) {
  1473  	coreDev := boottest.MockUC20Device("", nil)
  1474  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1475  
  1476  	m := &boot.Modeenv{
  1477  		Mode: "run",
  1478  		Base: s.base1.Filename(),
  1479  	}
  1480  	r := setupUC20Bootenv(
  1481  		c,
  1482  		s.bootloader,
  1483  		&bootenv20Setup{
  1484  			modeenv: m,
  1485  			// no kernel setup necessary
  1486  		},
  1487  	)
  1488  	defer r()
  1489  
  1490  	// get the boot base participant from our base snap
  1491  	bootBase := boot.Participant(s.base1, snap.TypeBase, coreDev)
  1492  	// make sure it's not a trivial boot participant
  1493  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1494  
  1495  	// make the base used on next boot
  1496  	rebootRequired, err := bootBase.SetNextBoot()
  1497  	c.Assert(err, IsNil)
  1498  
  1499  	// we don't need to reboot because it's the same base snap
  1500  	c.Assert(rebootRequired, Equals, false)
  1501  
  1502  	// make sure the modeenv wasn't changed
  1503  	m2, err := boot.ReadModeenv("")
  1504  	c.Assert(err, IsNil)
  1505  	c.Assert(m2.Base, Equals, m.Base)
  1506  	c.Assert(m2.BaseStatus, Equals, m.BaseStatus)
  1507  	c.Assert(m2.TryBase, Equals, m.TryBase)
  1508  }
  1509  
  1510  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnap(c *C) {
  1511  	coreDev := boottest.MockUC20Device("", nil)
  1512  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1513  
  1514  	// default state
  1515  	m := &boot.Modeenv{
  1516  		Mode: "run",
  1517  		Base: s.base1.Filename(),
  1518  	}
  1519  	r := setupUC20Bootenv(
  1520  		c,
  1521  		s.bootloader,
  1522  		&bootenv20Setup{
  1523  			modeenv: m,
  1524  			// no kernel setup necessary
  1525  		},
  1526  	)
  1527  	defer r()
  1528  
  1529  	// get the boot base participant from our new base snap
  1530  	bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev)
  1531  	// make sure it's not a trivial boot participant
  1532  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1533  
  1534  	// make the base used on next boot
  1535  	rebootRequired, err := bootBase.SetNextBoot()
  1536  	c.Assert(err, IsNil)
  1537  	c.Assert(rebootRequired, Equals, true)
  1538  
  1539  	// make sure the modeenv was updated
  1540  	m2, err := boot.ReadModeenv("")
  1541  	c.Assert(err, IsNil)
  1542  	c.Assert(m2.Base, Equals, m.Base)
  1543  	c.Assert(m2.BaseStatus, Equals, boot.TryStatus)
  1544  	c.Assert(m2.TryBase, Equals, s.base2.Filename())
  1545  }
  1546  
  1547  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnapNoReseal(c *C) {
  1548  	// checked by resealKeyToModeenv
  1549  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1550  
  1551  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1552  
  1553  	coreDev := boottest.MockUC20Device("", nil)
  1554  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1555  
  1556  	resealCalls := 0
  1557  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1558  		resealCalls++
  1559  		return nil
  1560  	})
  1561  	defer restore()
  1562  
  1563  	// we should not even need to build boot chains
  1564  	tab.BootChainErr = errors.New("boom")
  1565  
  1566  	// default state
  1567  	m := &boot.Modeenv{
  1568  		Mode: "run",
  1569  		Base: s.base1.Filename(),
  1570  	}
  1571  	r := setupUC20Bootenv(
  1572  		c,
  1573  		tab.MockBootloader,
  1574  		&bootenv20Setup{
  1575  			modeenv: m,
  1576  			// no kernel setup necessary
  1577  		},
  1578  	)
  1579  	defer r()
  1580  
  1581  	// get the boot base participant from our new base snap
  1582  	bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev)
  1583  	// make sure it's not a trivial boot participant
  1584  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1585  
  1586  	// make the base used on next boot
  1587  	rebootRequired, err := bootBase.SetNextBoot()
  1588  	c.Assert(err, IsNil)
  1589  	c.Assert(rebootRequired, Equals, true)
  1590  
  1591  	// make sure the modeenv was updated
  1592  	m2, err := boot.ReadModeenv("")
  1593  	c.Assert(err, IsNil)
  1594  	c.Assert(m2.Base, Equals, m.Base)
  1595  	c.Assert(m2.BaseStatus, Equals, boot.TryStatus)
  1596  	c.Assert(m2.TryBase, Equals, s.base2.Filename())
  1597  
  1598  	// no reseal
  1599  	c.Check(resealCalls, Equals, 0)
  1600  }
  1601  
  1602  func (s *bootenvSuite) TestMarkBootSuccessfulAllSnap(c *C) {
  1603  	coreDev := boottest.MockDevice("some-snap")
  1604  
  1605  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1606  	s.bootloader.BootVars["snap_try_core"] = "os1"
  1607  	s.bootloader.BootVars["snap_try_kernel"] = "k1"
  1608  	err := boot.MarkBootSuccessful(coreDev)
  1609  	c.Assert(err, IsNil)
  1610  
  1611  	expected := map[string]string{
  1612  		// cleared
  1613  		"snap_mode":       boot.DefaultStatus,
  1614  		"snap_try_kernel": "",
  1615  		"snap_try_core":   "",
  1616  		// updated
  1617  		"snap_kernel": "k1",
  1618  		"snap_core":   "os1",
  1619  	}
  1620  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1621  
  1622  	// do it again, verify its still valid
  1623  	err = boot.MarkBootSuccessful(coreDev)
  1624  	c.Assert(err, IsNil)
  1625  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1626  }
  1627  
  1628  func (s *bootenv20Suite) TestMarkBootSuccessful20AllSnap(c *C) {
  1629  	coreDev := boottest.MockUC20Device("", nil)
  1630  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1631  
  1632  	// bonus points: we were trying both a base snap and a kernel snap
  1633  	m := &boot.Modeenv{
  1634  		Mode:           "run",
  1635  		Base:           s.base1.Filename(),
  1636  		TryBase:        s.base2.Filename(),
  1637  		BaseStatus:     boot.TryingStatus,
  1638  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1639  	}
  1640  	r := setupUC20Bootenv(
  1641  		c,
  1642  		s.bootloader,
  1643  		&bootenv20Setup{
  1644  			modeenv:    m,
  1645  			kern:       s.kern1,
  1646  			tryKern:    s.kern2,
  1647  			kernStatus: boot.TryingStatus,
  1648  		},
  1649  	)
  1650  	defer r()
  1651  
  1652  	err := boot.MarkBootSuccessful(coreDev)
  1653  	c.Assert(err, IsNil)
  1654  
  1655  	// check the bootloader variables
  1656  	expected := map[string]string{
  1657  		// cleared
  1658  		"kernel_status": boot.DefaultStatus,
  1659  	}
  1660  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1661  
  1662  	// check that we called EnableKernel() on the try-kernel
  1663  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1664  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1665  
  1666  	// and that we disabled a try kernel
  1667  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1668  	c.Assert(nDisableTryCalls, Equals, 1)
  1669  
  1670  	// also check that the modeenv was updated
  1671  	m2, err := boot.ReadModeenv("")
  1672  	c.Assert(err, IsNil)
  1673  	c.Assert(m2.Base, Equals, s.base2.Filename())
  1674  	c.Assert(m2.TryBase, Equals, "")
  1675  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1676  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1677  
  1678  	// do it again, verify its still valid
  1679  	err = boot.MarkBootSuccessful(coreDev)
  1680  	c.Assert(err, IsNil)
  1681  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1682  
  1683  	// no new enabled kernels
  1684  	actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1685  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1686  	// we always disable the try kernel as a cleanup operation, so there's one
  1687  	// more call here
  1688  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1689  	c.Assert(nDisableTryCalls, Equals, 2)
  1690  }
  1691  
  1692  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20AllSnap(c *C) {
  1693  	coreDev := boottest.MockUC20Device("", nil)
  1694  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1695  
  1696  	// bonus points: we were trying both a base snap and a kernel snap
  1697  	m := &boot.Modeenv{
  1698  		Mode:           "run",
  1699  		Base:           s.base1.Filename(),
  1700  		TryBase:        s.base2.Filename(),
  1701  		BaseStatus:     boot.TryingStatus,
  1702  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1703  	}
  1704  	r := setupUC20Bootenv(
  1705  		c,
  1706  		s.bootloader,
  1707  		&bootenv20Setup{
  1708  			modeenv:    m,
  1709  			kern:       s.kern1,
  1710  			tryKern:    s.kern2,
  1711  			kernStatus: boot.TryingStatus,
  1712  		},
  1713  	)
  1714  	defer r()
  1715  
  1716  	err := boot.MarkBootSuccessful(coreDev)
  1717  	c.Assert(err, IsNil)
  1718  
  1719  	// check the bootloader variables
  1720  	expected := map[string]string{
  1721  		// cleared
  1722  		"kernel_status":   boot.DefaultStatus,
  1723  		"snap_try_kernel": "",
  1724  		// enabled new kernel
  1725  		"snap_kernel": s.kern2.Filename(),
  1726  	}
  1727  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1728  
  1729  	// also check that the modeenv was updated
  1730  	m2, err := boot.ReadModeenv("")
  1731  	c.Assert(err, IsNil)
  1732  	c.Assert(m2.Base, Equals, s.base2.Filename())
  1733  	c.Assert(m2.TryBase, Equals, "")
  1734  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1735  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1736  
  1737  	// do it again, verify its still valid
  1738  	err = boot.MarkBootSuccessful(coreDev)
  1739  	c.Assert(err, IsNil)
  1740  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1741  }
  1742  
  1743  func (s *bootenvSuite) TestMarkBootSuccessfulKernelUpdate(c *C) {
  1744  	coreDev := boottest.MockDevice("some-snap")
  1745  
  1746  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1747  	s.bootloader.BootVars["snap_core"] = "os1"
  1748  	s.bootloader.BootVars["snap_kernel"] = "k1"
  1749  	s.bootloader.BootVars["snap_try_core"] = ""
  1750  	s.bootloader.BootVars["snap_try_kernel"] = "k2"
  1751  	err := boot.MarkBootSuccessful(coreDev)
  1752  	c.Assert(err, IsNil)
  1753  	c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{
  1754  		// cleared
  1755  		"snap_mode":       boot.DefaultStatus,
  1756  		"snap_try_kernel": "",
  1757  		"snap_try_core":   "",
  1758  		// unchanged
  1759  		"snap_core": "os1",
  1760  		// updated
  1761  		"snap_kernel": "k2",
  1762  	})
  1763  }
  1764  
  1765  func (s *bootenvSuite) TestMarkBootSuccessfulBaseUpdate(c *C) {
  1766  	coreDev := boottest.MockDevice("some-snap")
  1767  
  1768  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1769  	s.bootloader.BootVars["snap_core"] = "os1"
  1770  	s.bootloader.BootVars["snap_kernel"] = "k1"
  1771  	s.bootloader.BootVars["snap_try_core"] = "os2"
  1772  	s.bootloader.BootVars["snap_try_kernel"] = ""
  1773  	err := boot.MarkBootSuccessful(coreDev)
  1774  	c.Assert(err, IsNil)
  1775  	c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{
  1776  		// cleared
  1777  		"snap_mode":     boot.DefaultStatus,
  1778  		"snap_try_core": "",
  1779  		// unchanged
  1780  		"snap_kernel":     "k1",
  1781  		"snap_try_kernel": "",
  1782  		// updated
  1783  		"snap_core": "os2",
  1784  	})
  1785  }
  1786  
  1787  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdate(c *C) {
  1788  	// trying a kernel snap
  1789  	m := &boot.Modeenv{
  1790  		Mode:           "run",
  1791  		Base:           s.base1.Filename(),
  1792  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1793  	}
  1794  	r := setupUC20Bootenv(
  1795  		c,
  1796  		s.bootloader,
  1797  		&bootenv20Setup{
  1798  			modeenv:    m,
  1799  			kern:       s.kern1,
  1800  			tryKern:    s.kern2,
  1801  			kernStatus: boot.TryingStatus,
  1802  		},
  1803  	)
  1804  	defer r()
  1805  
  1806  	coreDev := boottest.MockUC20Device("", nil)
  1807  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1808  
  1809  	// mark successful
  1810  	err := boot.MarkBootSuccessful(coreDev)
  1811  	c.Assert(err, IsNil)
  1812  
  1813  	// check the bootloader variables
  1814  	expected := map[string]string{"kernel_status": boot.DefaultStatus}
  1815  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1816  
  1817  	// check that MarkBootSuccessful enabled the try kernel
  1818  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1819  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1820  
  1821  	// and that we disabled a try kernel
  1822  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1823  	c.Assert(nDisableTryCalls, Equals, 1)
  1824  
  1825  	// check that the new kernel is the only one in modeenv
  1826  	m2, err := boot.ReadModeenv("")
  1827  	c.Assert(err, IsNil)
  1828  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1829  
  1830  	// do it again, verify its still valid
  1831  	err = boot.MarkBootSuccessful(coreDev)
  1832  	c.Assert(err, IsNil)
  1833  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1834  
  1835  	// no new bootloader calls
  1836  	actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1837  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1838  
  1839  	// we did disable the kernel again because we always do this to cleanup in
  1840  	// case there were leftovers
  1841  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1842  	c.Assert(nDisableTryCalls, Equals, 2)
  1843  }
  1844  
  1845  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdateWithReseal(c *C) {
  1846  	// checked by resealKeyToModeenv
  1847  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1848  
  1849  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1850  
  1851  	data := []byte("foobar")
  1852  	// SHA3-384
  1853  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1854  
  1855  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1856  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1857  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1858  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1859  
  1860  	// mock the files in cache
  1861  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1862  		"asset-" + dataHash,
  1863  	})
  1864  
  1865  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
  1866  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1867  
  1868  	tab.BootChainList = []bootloader.BootFile{
  1869  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1870  		runKernelBf,
  1871  	}
  1872  
  1873  	// trying a kernel snap
  1874  	m := &boot.Modeenv{
  1875  		Mode:           "run",
  1876  		Base:           s.base1.Filename(),
  1877  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1878  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1879  			"asset": {dataHash},
  1880  		},
  1881  	}
  1882  	r := setupUC20Bootenv(
  1883  		c,
  1884  		tab.MockBootloader,
  1885  		&bootenv20Setup{
  1886  			modeenv:    m,
  1887  			kern:       s.kern1,
  1888  			tryKern:    s.kern2,
  1889  			kernStatus: boot.TryingStatus,
  1890  		},
  1891  	)
  1892  	defer r()
  1893  
  1894  	coreDev := boottest.MockUC20Device("", nil)
  1895  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1896  
  1897  	resealCalls := 0
  1898  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1899  		resealCalls++
  1900  
  1901  		c.Assert(params.ModelParams, HasLen, 1)
  1902  		mp := params.ModelParams[0]
  1903  		c.Check(mp.Model, DeepEquals, coreDev.Model())
  1904  		for _, ch := range mp.EFILoadChains {
  1905  			printChain(c, ch, "-")
  1906  		}
  1907  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  1908  			secboot.NewLoadChain(assetBf,
  1909  				secboot.NewLoadChain(runKernelBf)),
  1910  		})
  1911  		return nil
  1912  	})
  1913  	defer restore()
  1914  
  1915  	// mark successful
  1916  	err := boot.MarkBootSuccessful(coreDev)
  1917  	c.Assert(err, IsNil)
  1918  
  1919  	// check the bootloader variables
  1920  	expected := map[string]string{
  1921  		"kernel_status":   boot.DefaultStatus,
  1922  		"snap_kernel":     s.kern2.Filename(),
  1923  		"snap_try_kernel": boot.DefaultStatus,
  1924  	}
  1925  	c.Assert(tab.BootVars, DeepEquals, expected)
  1926  
  1927  	// check that the new kernel is the only one in modeenv
  1928  	m2, err := boot.ReadModeenv("")
  1929  	c.Assert(err, IsNil)
  1930  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1931  
  1932  	c.Check(resealCalls, Equals, 1)
  1933  }
  1934  
  1935  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelUpdate(c *C) {
  1936  	// trying a kernel snap
  1937  	m := &boot.Modeenv{
  1938  		Mode:           "run",
  1939  		Base:           s.base1.Filename(),
  1940  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1941  	}
  1942  	r := setupUC20Bootenv(
  1943  		c,
  1944  		s.bootloader,
  1945  		&bootenv20Setup{
  1946  			modeenv:    m,
  1947  			kern:       s.kern1,
  1948  			tryKern:    s.kern2,
  1949  			kernStatus: boot.TryingStatus,
  1950  		},
  1951  	)
  1952  	defer r()
  1953  
  1954  	coreDev := boottest.MockUC20Device("", nil)
  1955  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1956  
  1957  	// mark successful
  1958  	err := boot.MarkBootSuccessful(coreDev)
  1959  	c.Assert(err, IsNil)
  1960  
  1961  	// check the bootloader variables
  1962  	expected := map[string]string{
  1963  		"kernel_status":   boot.DefaultStatus,
  1964  		"snap_kernel":     s.kern2.Filename(),
  1965  		"snap_try_kernel": "",
  1966  	}
  1967  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1968  
  1969  	// check that the new kernel is the only one in modeenv
  1970  	m2, err := boot.ReadModeenv("")
  1971  	c.Assert(err, IsNil)
  1972  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1973  
  1974  	// do it again, verify its still valid
  1975  	err = boot.MarkBootSuccessful(coreDev)
  1976  	c.Assert(err, IsNil)
  1977  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1978  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1979  }
  1980  
  1981  func (s *bootenv20Suite) TestMarkBootSuccessful20BaseUpdate(c *C) {
  1982  	// we were trying a base snap
  1983  	m := &boot.Modeenv{
  1984  		Mode:           "run",
  1985  		Base:           s.base1.Filename(),
  1986  		TryBase:        s.base2.Filename(),
  1987  		BaseStatus:     boot.TryingStatus,
  1988  		CurrentKernels: []string{s.kern1.Filename()},
  1989  	}
  1990  	r := setupUC20Bootenv(
  1991  		c,
  1992  		s.bootloader,
  1993  		&bootenv20Setup{
  1994  			modeenv:    m,
  1995  			kern:       s.kern1,
  1996  			kernStatus: boot.DefaultStatus,
  1997  		},
  1998  	)
  1999  	defer r()
  2000  
  2001  	coreDev := boottest.MockUC20Device("", nil)
  2002  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2003  
  2004  	// mark successful
  2005  	err := boot.MarkBootSuccessful(coreDev)
  2006  	c.Assert(err, IsNil)
  2007  
  2008  	// check the modeenv
  2009  	m2, err := boot.ReadModeenv("")
  2010  	c.Assert(err, IsNil)
  2011  	c.Assert(m2.Base, Equals, s.base2.Filename())
  2012  	c.Assert(m2.TryBase, Equals, "")
  2013  	c.Assert(m2.BaseStatus, Equals, "")
  2014  
  2015  	// do it again, verify its still valid
  2016  	err = boot.MarkBootSuccessful(coreDev)
  2017  	c.Assert(err, IsNil)
  2018  
  2019  	// check the modeenv again
  2020  	m3, err := boot.ReadModeenv("")
  2021  	c.Assert(err, IsNil)
  2022  	c.Assert(m3.Base, Equals, s.base2.Filename())
  2023  	c.Assert(m3.TryBase, Equals, "")
  2024  	c.Assert(m3.BaseStatus, Equals, "")
  2025  }
  2026  
  2027  func (s *bootenv20Suite) bootloaderWithTrustedAssets(c *C, trustedAssets []string) *bootloadertest.MockTrustedAssetsBootloader {
  2028  	// TODO:UC20: this should be an ExtractedRecoveryKernelImageBootloader
  2029  	// because that would reflect our main currently supported
  2030  	// trusted assets bootloader (grub)
  2031  	tab := bootloadertest.Mock("trusted", "").WithTrustedAssets()
  2032  	bootloader.Force(tab)
  2033  	tab.TrustedAssetsList = trustedAssets
  2034  	s.AddCleanup(func() { bootloader.Force(nil) })
  2035  	return tab
  2036  }
  2037  
  2038  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateHappy(c *C) {
  2039  	// checked by resealKeyToModeenv
  2040  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2041  
  2042  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset", "shim"})
  2043  
  2044  	data := []byte("foobar")
  2045  	// SHA3-384
  2046  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2047  	shim := []byte("shim")
  2048  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2049  
  2050  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755), IsNil)
  2051  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuSeedDir, 0755), IsNil)
  2052  	// only asset for ubuntu
  2053  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  2054  	// shim and asset for seed
  2055  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  2056  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2057  
  2058  	// mock the files in cache
  2059  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2060  		"shim-recoveryshimhash",
  2061  		"shim-" + shimHash,
  2062  		"asset-assethash",
  2063  		"asset-recoveryassethash",
  2064  		"asset-" + dataHash,
  2065  	})
  2066  
  2067  	shimBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("shim-%s", shimHash)), bootloader.RoleRecovery)
  2068  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRecovery)
  2069  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2070  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2071  
  2072  	tab.BootChainList = []bootloader.BootFile{
  2073  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2074  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2075  		runKernelBf,
  2076  	}
  2077  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2078  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2079  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2080  		recoveryKernelBf,
  2081  	}
  2082  
  2083  	uc20Model := boottest.MakeMockUC20Model()
  2084  
  2085  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2086  		kernelSnap := &seed.Snap{
  2087  			Path: "/var/lib/snapd/seed/snaps/pc-linux_1.snap",
  2088  			SideInfo: &snap.SideInfo{
  2089  				Revision: snap.Revision{N: 1},
  2090  				RealName: "pc-linux",
  2091  			},
  2092  		}
  2093  		return uc20Model, []*seed.Snap{kernelSnap}, nil
  2094  	})
  2095  	defer restore()
  2096  
  2097  	// we were trying an update of boot assets
  2098  	m := &boot.Modeenv{
  2099  		Mode:           "run",
  2100  		Base:           s.base1.Filename(),
  2101  		CurrentKernels: []string{s.kern1.Filename()},
  2102  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2103  			"asset": {"assethash", dataHash},
  2104  		},
  2105  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2106  			"asset": {"recoveryassethash", dataHash},
  2107  			"shim":  {"recoveryshimhash", shimHash},
  2108  		},
  2109  		CurrentRecoverySystems: []string{"system"},
  2110  	}
  2111  	r := setupUC20Bootenv(
  2112  		c,
  2113  		tab.MockBootloader,
  2114  		&bootenv20Setup{
  2115  			modeenv:    m,
  2116  			kern:       s.kern1,
  2117  			kernStatus: boot.DefaultStatus,
  2118  		},
  2119  	)
  2120  	defer r()
  2121  
  2122  	coreDev := boottest.MockUC20Device("", uc20Model)
  2123  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2124  
  2125  	resealCalls := 0
  2126  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2127  		resealCalls++
  2128  
  2129  		c.Assert(params.ModelParams, HasLen, 1)
  2130  		mp := params.ModelParams[0]
  2131  		c.Check(mp.Model, DeepEquals, uc20Model)
  2132  		for _, ch := range mp.EFILoadChains {
  2133  			printChain(c, ch, "-")
  2134  		}
  2135  		switch resealCalls {
  2136  		case 1:
  2137  			c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2138  				secboot.NewLoadChain(shimBf,
  2139  					secboot.NewLoadChain(assetBf,
  2140  						secboot.NewLoadChain(runKernelBf))),
  2141  				secboot.NewLoadChain(shimBf,
  2142  					secboot.NewLoadChain(assetBf,
  2143  						secboot.NewLoadChain(recoveryKernelBf))),
  2144  			})
  2145  		case 2:
  2146  			c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2147  				secboot.NewLoadChain(shimBf,
  2148  					secboot.NewLoadChain(assetBf,
  2149  						secboot.NewLoadChain(recoveryKernelBf))),
  2150  			})
  2151  		default:
  2152  			c.Errorf("unexpected additional call to secboot.ResealKey (call # %d)", resealCalls)
  2153  		}
  2154  		return nil
  2155  	})
  2156  	defer restore()
  2157  
  2158  	// mark successful
  2159  	err := boot.MarkBootSuccessful(coreDev)
  2160  	c.Assert(err, IsNil)
  2161  
  2162  	// check the modeenv
  2163  	m2, err := boot.ReadModeenv("")
  2164  	c.Assert(err, IsNil)
  2165  	// update assets are in the list
  2166  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, boot.BootAssetsMap{
  2167  		"asset": {dataHash},
  2168  	})
  2169  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, boot.BootAssetsMap{
  2170  		"asset": {dataHash},
  2171  		"shim":  {shimHash},
  2172  	})
  2173  	// unused files were dropped from cache
  2174  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2175  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2176  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2177  	})
  2178  	c.Check(resealCalls, Equals, 2)
  2179  }
  2180  
  2181  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsStableStateHappy(c *C) {
  2182  	// checked by resealKeyToModeenv
  2183  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2184  
  2185  	tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"})
  2186  
  2187  	data := []byte("foobar")
  2188  	// SHA3-384
  2189  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2190  	shim := []byte("shim")
  2191  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2192  
  2193  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil)
  2194  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil)
  2195  	// only asset for ubuntu-boot
  2196  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil)
  2197  	// shim and asset for ubuntu-seed
  2198  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil)
  2199  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2200  
  2201  	// mock the files in cache
  2202  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2203  		"shim-" + shimHash,
  2204  		"asset-" + dataHash,
  2205  	})
  2206  
  2207  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2208  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2209  
  2210  	tab.BootChainList = []bootloader.BootFile{
  2211  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2212  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2213  		runKernelBf,
  2214  	}
  2215  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2216  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2217  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2218  		recoveryKernelBf,
  2219  	}
  2220  
  2221  	uc20Model := boottest.MakeMockUC20Model()
  2222  
  2223  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2224  		kernelSnap := &seed.Snap{
  2225  			Path: "/var/lib/snapd/seed/snaps/pc-kernel-recovery_1.snap",
  2226  			SideInfo: &snap.SideInfo{
  2227  				Revision: snap.Revision{N: 1},
  2228  				RealName: "pc-kernel-recovery",
  2229  			},
  2230  		}
  2231  		return uc20Model, []*seed.Snap{kernelSnap}, nil
  2232  	})
  2233  	defer restore()
  2234  
  2235  	// we were trying an update of boot assets
  2236  	m := &boot.Modeenv{
  2237  		Mode:           "run",
  2238  		Base:           s.base1.Filename(),
  2239  		CurrentKernels: []string{s.kern1.Filename()},
  2240  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2241  			"asset": {dataHash},
  2242  		},
  2243  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2244  			"asset": {dataHash},
  2245  			"shim":  {shimHash},
  2246  		},
  2247  		CurrentRecoverySystems:    []string{"system"},
  2248  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  2249  	}
  2250  	r := setupUC20Bootenv(
  2251  		c,
  2252  		tab.MockBootloader,
  2253  		&bootenv20Setup{
  2254  			modeenv:    m,
  2255  			kern:       s.kern1,
  2256  			kernStatus: boot.DefaultStatus,
  2257  		},
  2258  	)
  2259  	defer r()
  2260  
  2261  	coreDev := boottest.MockUC20Device("", uc20Model)
  2262  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2263  
  2264  	resealCalls := 0
  2265  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2266  		resealCalls++
  2267  		return nil
  2268  	})
  2269  	defer restore()
  2270  
  2271  	// write boot-chains for current state that will stay unchanged
  2272  	bootChains := []boot.BootChain{{
  2273  		BrandID:        "my-brand",
  2274  		Model:          "my-model-uc20",
  2275  		Grade:          "dangerous",
  2276  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2277  		AssetChain: []boot.BootAsset{{
  2278  			Role: bootloader.RoleRecovery, Name: "shim",
  2279  			Hashes: []string{
  2280  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2281  			},
  2282  		}, {
  2283  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2284  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2285  			},
  2286  		}},
  2287  		Kernel:         "pc-kernel",
  2288  		KernelRevision: "1",
  2289  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  2290  	}, {
  2291  		BrandID:        "my-brand",
  2292  		Model:          "my-model-uc20",
  2293  		Grade:          "dangerous",
  2294  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2295  		AssetChain: []boot.BootAsset{{
  2296  			Role: bootloader.RoleRecovery, Name: "shim",
  2297  			Hashes: []string{
  2298  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2299  			},
  2300  		}, {
  2301  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2302  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2303  			},
  2304  		}},
  2305  		Kernel:         "pc-kernel-recovery",
  2306  		KernelRevision: "1",
  2307  		KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"},
  2308  	}}
  2309  
  2310  	recoveryBootChains := []boot.BootChain{bootChains[1]}
  2311  
  2312  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  2313  	c.Assert(err, IsNil)
  2314  
  2315  	err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0)
  2316  	c.Assert(err, IsNil)
  2317  
  2318  	// mark successful
  2319  	err = boot.MarkBootSuccessful(coreDev)
  2320  	c.Assert(err, IsNil)
  2321  
  2322  	// modeenv is unchanged
  2323  	m2, err := boot.ReadModeenv("")
  2324  	c.Assert(err, IsNil)
  2325  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2326  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2327  	// files are still in cache
  2328  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2329  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2330  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2331  	})
  2332  
  2333  	// boot chains were built
  2334  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  2335  		s.kern1.MountFile(),
  2336  	})
  2337  	// no actual reseal
  2338  	c.Check(resealCalls, Equals, 0)
  2339  }
  2340  
  2341  func (s *bootenv20Suite) TestMarkBootSuccessful20BootUnassertedKernelAssetsStableStateHappy(c *C) {
  2342  	// checked by resealKeyToModeenv
  2343  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2344  
  2345  	tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"})
  2346  
  2347  	data := []byte("foobar")
  2348  	// SHA3-384
  2349  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2350  	shim := []byte("shim")
  2351  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2352  
  2353  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil)
  2354  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil)
  2355  	// only asset for ubuntu-boot
  2356  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil)
  2357  	// shim and asset for ubuntu-seed
  2358  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil)
  2359  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2360  
  2361  	// mock the files in cache
  2362  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2363  		"shim-" + shimHash,
  2364  		"asset-" + dataHash,
  2365  	})
  2366  
  2367  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2368  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2369  
  2370  	tab.BootChainList = []bootloader.BootFile{
  2371  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2372  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2373  		runKernelBf,
  2374  	}
  2375  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2376  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2377  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2378  		recoveryKernelBf,
  2379  	}
  2380  
  2381  	uc20Model := boottest.MakeMockUC20Model()
  2382  
  2383  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2384  		kernelSnap := &seed.Snap{
  2385  			Path: "/var/lib/snapd/seed/snaps/pc-kernel-recovery_1.snap",
  2386  			SideInfo: &snap.SideInfo{
  2387  				Revision: snap.Revision{N: 1},
  2388  				RealName: "pc-kernel-recovery",
  2389  			},
  2390  		}
  2391  		return uc20Model, []*seed.Snap{kernelSnap}, nil
  2392  	})
  2393  	defer restore()
  2394  
  2395  	// we were trying an update of boot assets
  2396  	m := &boot.Modeenv{
  2397  		Mode:           "run",
  2398  		Base:           s.base1.Filename(),
  2399  		CurrentKernels: []string{s.ukern1.Filename()},
  2400  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2401  			"asset": {dataHash},
  2402  		},
  2403  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2404  			"asset": {dataHash},
  2405  			"shim":  {shimHash},
  2406  		},
  2407  		CurrentRecoverySystems:    []string{"system"},
  2408  		GoodRecoverySystems:       []string{"system"},
  2409  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  2410  	}
  2411  	r := setupUC20Bootenv(
  2412  		c,
  2413  		tab.MockBootloader,
  2414  		&bootenv20Setup{
  2415  			modeenv:    m,
  2416  			kern:       s.ukern1,
  2417  			kernStatus: boot.DefaultStatus,
  2418  		},
  2419  	)
  2420  	defer r()
  2421  
  2422  	coreDev := boottest.MockUC20Device("", uc20Model)
  2423  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2424  
  2425  	resealCalls := 0
  2426  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2427  		resealCalls++
  2428  		return nil
  2429  	})
  2430  	defer restore()
  2431  
  2432  	// write boot-chains for current state that will stay unchanged
  2433  	bootChains := []boot.BootChain{{
  2434  		BrandID:        "my-brand",
  2435  		Model:          "my-model-uc20",
  2436  		Grade:          "dangerous",
  2437  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2438  		AssetChain: []boot.BootAsset{{
  2439  			Role: bootloader.RoleRecovery, Name: "shim",
  2440  			Hashes: []string{
  2441  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2442  			},
  2443  		}, {
  2444  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2445  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2446  			},
  2447  		}},
  2448  		Kernel: "pc-kernel",
  2449  		// unasserted kernel snap
  2450  		KernelRevision: "",
  2451  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  2452  	}, {
  2453  		BrandID:        "my-brand",
  2454  		Model:          "my-model-uc20",
  2455  		Grade:          "dangerous",
  2456  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2457  		AssetChain: []boot.BootAsset{{
  2458  			Role: bootloader.RoleRecovery, Name: "shim",
  2459  			Hashes: []string{
  2460  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2461  			},
  2462  		}, {
  2463  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2464  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2465  			},
  2466  		}},
  2467  		Kernel:         "pc-kernel-recovery",
  2468  		KernelRevision: "1",
  2469  		KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"},
  2470  	}}
  2471  
  2472  	recoveryBootChains := []boot.BootChain{bootChains[1]}
  2473  
  2474  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  2475  	c.Assert(err, IsNil)
  2476  
  2477  	err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0)
  2478  	c.Assert(err, IsNil)
  2479  
  2480  	// mark successful
  2481  	err = boot.MarkBootSuccessful(coreDev)
  2482  	c.Assert(err, IsNil)
  2483  
  2484  	// modeenv is unchanged
  2485  	m2, err := boot.ReadModeenv("")
  2486  	c.Assert(err, IsNil)
  2487  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2488  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2489  	// files are still in cache
  2490  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2491  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2492  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2493  	})
  2494  
  2495  	// boot chains were built
  2496  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  2497  		s.ukern1.MountFile(),
  2498  	})
  2499  	// no actual reseal
  2500  	c.Check(resealCalls, Equals, 0)
  2501  }
  2502  
  2503  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateUnexpectedAsset(c *C) {
  2504  	tab := s.bootloaderWithTrustedAssets(c, []string{"EFI/asset"})
  2505  
  2506  	data := []byte("foobar")
  2507  	// SHA3-384
  2508  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2509  
  2510  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI"), 0755), IsNil)
  2511  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI"), 0755), IsNil)
  2512  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/asset"), data, 0644), IsNil)
  2513  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/asset"), data, 0644), IsNil)
  2514  	// mock some state in the cache
  2515  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2516  		"asset-one",
  2517  		"asset-two",
  2518  	})
  2519  
  2520  	// we were trying an update of boot assets
  2521  	m := &boot.Modeenv{
  2522  		Mode:           "run",
  2523  		Base:           s.base1.Filename(),
  2524  		CurrentKernels: []string{s.kern1.Filename()},
  2525  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2526  			// hash will not match
  2527  			"asset": {"one", "two"},
  2528  		},
  2529  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2530  			"asset": {"one", "two"},
  2531  		},
  2532  	}
  2533  	r := setupUC20Bootenv(
  2534  		c,
  2535  		tab.MockBootloader,
  2536  		&bootenv20Setup{
  2537  			modeenv:    m,
  2538  			kern:       s.kern1,
  2539  			kernStatus: boot.DefaultStatus,
  2540  		},
  2541  	)
  2542  	defer r()
  2543  
  2544  	coreDev := boottest.MockUC20Device("", nil)
  2545  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2546  
  2547  	// mark successful
  2548  	err := boot.MarkBootSuccessful(coreDev)
  2549  	c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot mark boot successful: cannot mark successful boot assets: system booted with unexpected run mode bootloader asset "EFI/asset" hash %s`, dataHash))
  2550  
  2551  	// check the modeenv
  2552  	m2, err := boot.ReadModeenv("")
  2553  	c.Assert(err, IsNil)
  2554  	// modeenv is unchaged
  2555  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2556  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2557  	// nothing was removed from cache
  2558  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2559  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-one"),
  2560  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-two"),
  2561  	})
  2562  }
  2563  
  2564  func (s *bootenv20Suite) setupMarkBootSuccessful20CommandLine(c *C, mode string, cmdlines boot.BootCommandLines) *boot.Modeenv {
  2565  	// mock some state in the cache
  2566  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2567  		"asset-one",
  2568  	})
  2569  	// a pending kernel command line change
  2570  	m := &boot.Modeenv{
  2571  		Mode:           mode,
  2572  		Base:           s.base1.Filename(),
  2573  		CurrentKernels: []string{s.kern1.Filename()},
  2574  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2575  			"asset": {"one"},
  2576  		},
  2577  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2578  			"asset": {"one"},
  2579  		},
  2580  		CurrentKernelCommandLines: cmdlines,
  2581  	}
  2582  	return m
  2583  }
  2584  
  2585  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedHappy(c *C) {
  2586  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2587  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2588  	m := s.setupMarkBootSuccessful20CommandLine(c, "run", boot.BootCommandLines{
  2589  		"snapd_recovery_mode=run panic=-1",
  2590  		"snapd_recovery_mode=run candidate panic=-1",
  2591  	})
  2592  
  2593  	r := setupUC20Bootenv(
  2594  		c,
  2595  		tab.MockBootloader,
  2596  		&bootenv20Setup{
  2597  			modeenv:    m,
  2598  			kern:       s.kern1,
  2599  			kernStatus: boot.DefaultStatus,
  2600  		},
  2601  	)
  2602  	defer r()
  2603  
  2604  	coreDev := boottest.MockUC20Device("", nil)
  2605  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2606  	// mark successful
  2607  	err := boot.MarkBootSuccessful(coreDev)
  2608  	c.Assert(err, IsNil)
  2609  
  2610  	// check the modeenv
  2611  	m2, err := boot.ReadModeenv("")
  2612  	c.Assert(err, IsNil)
  2613  	// modeenv is unchaged
  2614  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2615  		"snapd_recovery_mode=run candidate panic=-1",
  2616  	})
  2617  }
  2618  
  2619  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedOld(c *C) {
  2620  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1")
  2621  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2622  	m := s.setupMarkBootSuccessful20CommandLine(c, "run", boot.BootCommandLines{
  2623  		"snapd_recovery_mode=run panic=-1",
  2624  		"snapd_recovery_mode=run candidate panic=-1",
  2625  	})
  2626  	r := setupUC20Bootenv(
  2627  		c,
  2628  		tab.MockBootloader,
  2629  		&bootenv20Setup{
  2630  			modeenv:    m,
  2631  			kern:       s.kern1,
  2632  			kernStatus: boot.DefaultStatus,
  2633  		},
  2634  	)
  2635  	defer r()
  2636  
  2637  	coreDev := boottest.MockUC20Device("", nil)
  2638  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2639  	// mark successful
  2640  	err := boot.MarkBootSuccessful(coreDev)
  2641  	c.Assert(err, IsNil)
  2642  
  2643  	// check the modeenv
  2644  	m2, err := boot.ReadModeenv("")
  2645  	c.Assert(err, IsNil)
  2646  	// modeenv is unchaged
  2647  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2648  		"snapd_recovery_mode=run panic=-1",
  2649  	})
  2650  }
  2651  
  2652  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedMismatch(c *C) {
  2653  	s.mockCmdline(c, "snapd_recovery_mode=run different")
  2654  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2655  	m := s.setupMarkBootSuccessful20CommandLine(c, "run", boot.BootCommandLines{
  2656  		"snapd_recovery_mode=run",
  2657  		"snapd_recovery_mode=run candidate",
  2658  	})
  2659  	r := setupUC20Bootenv(
  2660  		c,
  2661  		tab.MockBootloader,
  2662  		&bootenv20Setup{
  2663  			modeenv:    m,
  2664  			kern:       s.kern1,
  2665  			kernStatus: boot.DefaultStatus,
  2666  		},
  2667  	)
  2668  	defer r()
  2669  
  2670  	coreDev := boottest.MockUC20Device("", nil)
  2671  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2672  	// mark successful
  2673  	err := boot.MarkBootSuccessful(coreDev)
  2674  	c.Assert(err, ErrorMatches, `cannot mark boot successful: cannot mark successful boot command line: current command line content "snapd_recovery_mode=run different" not matching any expected entry`)
  2675  }
  2676  
  2677  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootSuccessful(c *C) {
  2678  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1")
  2679  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2680  	tab.StaticCommandLine = "panic=-1"
  2681  	m := s.setupMarkBootSuccessful20CommandLine(c, "run", nil)
  2682  	r := setupUC20Bootenv(
  2683  		c,
  2684  		tab.MockBootloader,
  2685  		&bootenv20Setup{
  2686  			modeenv:    m,
  2687  			kern:       s.kern1,
  2688  			kernStatus: boot.DefaultStatus,
  2689  		},
  2690  	)
  2691  	defer r()
  2692  
  2693  	coreDev := boottest.MockUC20Device("", nil)
  2694  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2695  	// mark successful
  2696  	err := boot.MarkBootSuccessful(coreDev)
  2697  	c.Assert(err, IsNil)
  2698  
  2699  	// check the modeenv
  2700  	m2, err := boot.ReadModeenv("")
  2701  	c.Assert(err, IsNil)
  2702  	// modeenv is unchaged
  2703  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2704  		"snapd_recovery_mode=run panic=-1",
  2705  	})
  2706  }
  2707  
  2708  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootMismatch(c *C) {
  2709  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1 unexpected")
  2710  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2711  	tab.StaticCommandLine = "panic=-1"
  2712  	m := s.setupMarkBootSuccessful20CommandLine(c, "run", nil)
  2713  	r := setupUC20Bootenv(
  2714  		c,
  2715  		tab.MockBootloader,
  2716  		&bootenv20Setup{
  2717  			modeenv:    m,
  2718  			kern:       s.kern1,
  2719  			kernStatus: boot.DefaultStatus,
  2720  		},
  2721  	)
  2722  	defer r()
  2723  
  2724  	coreDev := boottest.MockUC20Device("", nil)
  2725  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2726  	// mark successful
  2727  	err := boot.MarkBootSuccessful(coreDev)
  2728  	c.Assert(err, ErrorMatches, `cannot mark boot successful: cannot mark successful boot command line: unexpected current command line: "snapd_recovery_mode=run panic=-1 unexpected"`)
  2729  }
  2730  
  2731  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineNonRunMode(c *C) {
  2732  	// recover mode
  2733  	s.mockCmdline(c, "snapd_recovery_mode=recover snapd_recovery_system=1234 panic=-1")
  2734  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2735  	tab.StaticCommandLine = "panic=-1"
  2736  	// current command line does not match any of the run mode command lines
  2737  	m := s.setupMarkBootSuccessful20CommandLine(c, "recover", boot.BootCommandLines{
  2738  		"snapd_recovery_mode=run panic=-1",
  2739  		"snapd_recovery_mode=run candidate panic=-1",
  2740  	})
  2741  	r := setupUC20Bootenv(
  2742  		c,
  2743  		tab.MockBootloader,
  2744  		&bootenv20Setup{
  2745  			modeenv:    m,
  2746  			kern:       s.kern1,
  2747  			kernStatus: boot.DefaultStatus,
  2748  		},
  2749  	)
  2750  	defer r()
  2751  
  2752  	coreDev := boottest.MockUC20Device("", nil)
  2753  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2754  	// mark successful
  2755  	err := boot.MarkBootSuccessful(coreDev)
  2756  	c.Assert(err, IsNil)
  2757  
  2758  	// check the modeenv
  2759  	m2, err := boot.ReadModeenv("")
  2760  	c.Assert(err, IsNil)
  2761  	// modeenv is unchaged
  2762  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2763  		"snapd_recovery_mode=run panic=-1",
  2764  		"snapd_recovery_mode=run candidate panic=-1",
  2765  	})
  2766  }
  2767  
  2768  func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsCompat(c *C) {
  2769  	b := bootloadertest.Mock("mock", s.bootdir)
  2770  	s.forceBootloader(b)
  2771  
  2772  	m := &boot.Modeenv{
  2773  		Mode:                   "run",
  2774  		Base:                   s.base1.Filename(),
  2775  		CurrentKernels:         []string{s.kern1.Filename()},
  2776  		CurrentRecoverySystems: []string{"1234"},
  2777  	}
  2778  
  2779  	r := setupUC20Bootenv(
  2780  		c,
  2781  		b,
  2782  		&bootenv20Setup{
  2783  			modeenv:    m,
  2784  			kern:       s.kern1,
  2785  			kernStatus: boot.DefaultStatus,
  2786  		},
  2787  	)
  2788  	defer r()
  2789  
  2790  	coreDev := boottest.MockUC20Device("", nil)
  2791  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2792  	// mark successful
  2793  	err := boot.MarkBootSuccessful(coreDev)
  2794  	c.Assert(err, IsNil)
  2795  
  2796  	// check the modeenv
  2797  	m2, err := boot.ReadModeenv("")
  2798  	c.Assert(err, IsNil)
  2799  	// the list of good recovery systems has not been modified
  2800  	c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"})
  2801  	c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234"})
  2802  }
  2803  
  2804  func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsPopulated(c *C) {
  2805  	b := bootloadertest.Mock("mock", s.bootdir)
  2806  	s.forceBootloader(b)
  2807  
  2808  	m := &boot.Modeenv{
  2809  		Mode:                   "run",
  2810  		Base:                   s.base1.Filename(),
  2811  		CurrentKernels:         []string{s.kern1.Filename()},
  2812  		CurrentRecoverySystems: []string{"1234", "9999"},
  2813  		GoodRecoverySystems:    []string{"1234"},
  2814  	}
  2815  
  2816  	r := setupUC20Bootenv(
  2817  		c,
  2818  		b,
  2819  		&bootenv20Setup{
  2820  			modeenv:    m,
  2821  			kern:       s.kern1,
  2822  			kernStatus: boot.DefaultStatus,
  2823  		},
  2824  	)
  2825  	defer r()
  2826  
  2827  	coreDev := boottest.MockUC20Device("", nil)
  2828  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2829  	// mark successful
  2830  	err := boot.MarkBootSuccessful(coreDev)
  2831  	c.Assert(err, IsNil)
  2832  
  2833  	// check the modeenv
  2834  	m2, err := boot.ReadModeenv("")
  2835  	c.Assert(err, IsNil)
  2836  	// good recovery systems has been populated
  2837  	c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"})
  2838  	c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234", "9999"})
  2839  }
  2840  
  2841  type recoveryBootenv20Suite struct {
  2842  	baseBootenvSuite
  2843  
  2844  	bootloader *bootloadertest.MockBootloader
  2845  
  2846  	dev boot.Device
  2847  }
  2848  
  2849  var _ = Suite(&recoveryBootenv20Suite{})
  2850  
  2851  func (s *recoveryBootenv20Suite) SetUpTest(c *C) {
  2852  	s.baseBootenvSuite.SetUpTest(c)
  2853  
  2854  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
  2855  	s.forceBootloader(s.bootloader)
  2856  
  2857  	s.dev = boottest.MockUC20Device("", nil)
  2858  }
  2859  
  2860  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeHappy(c *C) {
  2861  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  2862  	c.Assert(err, IsNil)
  2863  	c.Check(s.bootloader.BootVars, DeepEquals, map[string]string{
  2864  		"snapd_recovery_system": "1234",
  2865  		"snapd_recovery_mode":   "install",
  2866  	})
  2867  }
  2868  
  2869  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeSetErr(c *C) {
  2870  	s.bootloader.SetErr = errors.New("no can do")
  2871  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  2872  	c.Assert(err, ErrorMatches, `no can do`)
  2873  }
  2874  
  2875  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeNonUC20(c *C) {
  2876  	non20Dev := boottest.MockDevice("some-snap")
  2877  	err := boot.SetRecoveryBootSystemAndMode(non20Dev, "1234", "install")
  2878  	c.Assert(err, Equals, boot.ErrUnsupportedSystemMode)
  2879  }
  2880  
  2881  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeErrClumsy(c *C) {
  2882  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "", "install")
  2883  	c.Assert(err, ErrorMatches, "internal error: system label is unset")
  2884  	err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "")
  2885  	c.Assert(err, ErrorMatches, "internal error: system mode is unset")
  2886  }
  2887  
  2888  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeRealHappy(c *C) {
  2889  	bootloader.Force(nil)
  2890  
  2891  	mockSeedGrubDir := filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI", "ubuntu")
  2892  	err := os.MkdirAll(mockSeedGrubDir, 0755)
  2893  	c.Assert(err, IsNil)
  2894  	err = ioutil.WriteFile(filepath.Join(mockSeedGrubDir, "grub.cfg"), nil, 0644)
  2895  	c.Assert(err, IsNil)
  2896  
  2897  	err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  2898  	c.Assert(err, IsNil)
  2899  
  2900  	bl, err := bootloader.Find(boot.InitramfsUbuntuSeedDir, &bootloader.Options{Role: bootloader.RoleRecovery})
  2901  	c.Assert(err, IsNil)
  2902  
  2903  	blvars, err := bl.GetBootVars("snapd_recovery_mode", "snapd_recovery_system")
  2904  	c.Assert(err, IsNil)
  2905  	c.Check(blvars, DeepEquals, map[string]string{
  2906  		"snapd_recovery_system": "1234",
  2907  		"snapd_recovery_mode":   "install",
  2908  	})
  2909  }
  2910  
  2911  type bootConfigSuite struct {
  2912  	baseBootenvSuite
  2913  
  2914  	bootloader *bootloadertest.MockTrustedAssetsBootloader
  2915  }
  2916  
  2917  var _ = Suite(&bootConfigSuite{})
  2918  
  2919  func (s *bootConfigSuite) SetUpTest(c *C) {
  2920  	s.baseBootenvSuite.SetUpTest(c)
  2921  
  2922  	s.bootloader = bootloadertest.Mock("trusted", c.MkDir()).WithTrustedAssets()
  2923  	s.bootloader.StaticCommandLine = "this is mocked panic=-1"
  2924  	s.bootloader.CandidateStaticCommandLine = "mocked candidate panic=-1"
  2925  	s.forceBootloader(s.bootloader)
  2926  
  2927  	s.mockCmdline(c, "snapd_recovery_mode=run this is mocked panic=-1")
  2928  }
  2929  
  2930  func (s *bootConfigSuite) mockCmdline(c *C, cmdline string) {
  2931  	c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil)
  2932  }
  2933  
  2934  func (s *bootConfigSuite) TestBootConfigUpdateHappyNoKeysNoReseal(c *C) {
  2935  	coreDev := boottest.MockUC20Device("", nil)
  2936  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2937  
  2938  	m := &boot.Modeenv{
  2939  		Mode: "run",
  2940  		CurrentKernelCommandLines: boot.BootCommandLines{
  2941  			"snapd_recovery_mode=run this is mocked panic=-1",
  2942  		},
  2943  	}
  2944  	c.Assert(m.WriteTo(""), IsNil)
  2945  
  2946  	resealCalls := 0
  2947  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2948  		resealCalls++
  2949  		return nil
  2950  	})
  2951  	defer restore()
  2952  
  2953  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  2954  	c.Assert(err, IsNil)
  2955  	c.Check(updated, Equals, false)
  2956  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  2957  	c.Check(resealCalls, Equals, 0)
  2958  }
  2959  
  2960  func (s *bootConfigSuite) TestBootConfigUpdateHappyWithReseal(c *C) {
  2961  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2962  
  2963  	coreDev := boottest.MockUC20Device("", nil)
  2964  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2965  
  2966  	runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  2967  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2968  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2969  		"asset-hash-1",
  2970  	})
  2971  
  2972  	s.bootloader.TrustedAssetsList = []string{"asset"}
  2973  	s.bootloader.BootChainList = []bootloader.BootFile{
  2974  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  2975  		runKernelBf,
  2976  	}
  2977  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  2978  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2979  		recoveryKernelBf,
  2980  	}
  2981  	m := &boot.Modeenv{
  2982  		Mode:           "run",
  2983  		CurrentKernels: []string{"pc-kernel_500.snap"},
  2984  		CurrentKernelCommandLines: boot.BootCommandLines{
  2985  			"snapd_recovery_mode=run this is mocked panic=-1",
  2986  		},
  2987  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2988  			"asset": []string{"hash-1"},
  2989  		},
  2990  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2991  			"asset": []string{"hash-1"},
  2992  		},
  2993  	}
  2994  	c.Assert(m.WriteTo(""), IsNil)
  2995  
  2996  	resealCalls := 0
  2997  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2998  		resealCalls++
  2999  		c.Assert(params, NotNil)
  3000  		c.Assert(params.ModelParams, HasLen, 1)
  3001  		c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{
  3002  			"snapd_recovery_mode=run mocked candidate panic=-1",
  3003  			"snapd_recovery_mode=run this is mocked panic=-1",
  3004  		})
  3005  		return nil
  3006  	})
  3007  	defer restore()
  3008  
  3009  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  3010  	c.Assert(err, IsNil)
  3011  	c.Check(updated, Equals, false)
  3012  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3013  	c.Check(resealCalls, Equals, 1)
  3014  
  3015  	m2, err := boot.ReadModeenv("")
  3016  	c.Assert(err, IsNil)
  3017  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3018  		"snapd_recovery_mode=run this is mocked panic=-1",
  3019  		"snapd_recovery_mode=run mocked candidate panic=-1",
  3020  	})
  3021  }
  3022  
  3023  func (s *bootConfigSuite) TestBootConfigUpdateHappyNoChange(c *C) {
  3024  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3025  
  3026  	coreDev := boottest.MockUC20Device("", nil)
  3027  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3028  
  3029  	s.bootloader.StaticCommandLine = "mocked unchanged panic=-1"
  3030  	s.bootloader.CandidateStaticCommandLine = "mocked unchanged panic=-1"
  3031  	s.mockCmdline(c, "snapd_recovery_mode=run mocked unchanged panic=-1")
  3032  
  3033  	m := &boot.Modeenv{
  3034  		Mode: "run",
  3035  		CurrentKernelCommandLines: boot.BootCommandLines{
  3036  			"snapd_recovery_mode=run mocked unchanged panic=-1",
  3037  		},
  3038  	}
  3039  	c.Assert(m.WriteTo(""), IsNil)
  3040  
  3041  	resealCalls := 0
  3042  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3043  		resealCalls++
  3044  		return nil
  3045  	})
  3046  	defer restore()
  3047  
  3048  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  3049  	c.Assert(err, IsNil)
  3050  	c.Check(updated, Equals, false)
  3051  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3052  	c.Check(resealCalls, Equals, 0)
  3053  
  3054  	m2, err := boot.ReadModeenv("")
  3055  	c.Assert(err, IsNil)
  3056  	c.Assert(m2.CurrentKernelCommandLines, HasLen, 1)
  3057  }
  3058  
  3059  func (s *bootConfigSuite) TestBootConfigUpdateNonUC20DoesNothing(c *C) {
  3060  	nonUC20coreDev := boottest.MockDevice("pc-kernel")
  3061  	c.Assert(nonUC20coreDev.HasModeenv(), Equals, false)
  3062  	updated, err := boot.UpdateManagedBootConfigs(nonUC20coreDev)
  3063  	c.Assert(err, IsNil)
  3064  	c.Check(updated, Equals, false)
  3065  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3066  }
  3067  
  3068  func (s *bootConfigSuite) TestBootConfigUpdateBadModeErr(c *C) {
  3069  	uc20Dev := boottest.MockUC20Device("recover", nil)
  3070  	c.Assert(uc20Dev.HasModeenv(), Equals, true)
  3071  	updated, err := boot.UpdateManagedBootConfigs(uc20Dev)
  3072  	c.Assert(err, ErrorMatches, "internal error: boot config can only be updated in run mode")
  3073  	c.Check(updated, Equals, false)
  3074  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3075  }
  3076  
  3077  func (s *bootConfigSuite) TestBootConfigUpdateFailErr(c *C) {
  3078  	coreDev := boottest.MockUC20Device("", nil)
  3079  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3080  
  3081  	m := &boot.Modeenv{
  3082  		Mode: "run",
  3083  		CurrentKernelCommandLines: boot.BootCommandLines{
  3084  			"snapd_recovery_mode=run this is mocked panic=-1",
  3085  		},
  3086  	}
  3087  	c.Assert(m.WriteTo(""), IsNil)
  3088  
  3089  	s.bootloader.UpdateErr = errors.New("update fail")
  3090  
  3091  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  3092  	c.Assert(err, ErrorMatches, "update fail")
  3093  	c.Check(updated, Equals, false)
  3094  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3095  }
  3096  
  3097  func (s *bootConfigSuite) TestBootConfigUpdateCmdlineMismatchErr(c *C) {
  3098  	coreDev := boottest.MockUC20Device("", nil)
  3099  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3100  
  3101  	m := &boot.Modeenv{
  3102  		Mode: "run",
  3103  	}
  3104  	c.Assert(m.WriteTo(""), IsNil)
  3105  
  3106  	s.mockCmdline(c, "snapd_recovery_mode=run unexpected cmdline")
  3107  
  3108  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  3109  	c.Assert(err, ErrorMatches, `internal error: current kernel command lines is unset`)
  3110  	c.Check(updated, Equals, false)
  3111  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3112  }
  3113  
  3114  func (s *bootConfigSuite) TestBootConfigUpdateNotManagedErr(c *C) {
  3115  	coreDev := boottest.MockUC20Device("", nil)
  3116  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3117  
  3118  	bl := bootloadertest.Mock("not-managed", c.MkDir())
  3119  	bootloader.Force(bl)
  3120  	defer bootloader.Force(nil)
  3121  
  3122  	m := &boot.Modeenv{
  3123  		Mode: "run",
  3124  	}
  3125  	c.Assert(m.WriteTo(""), IsNil)
  3126  
  3127  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  3128  	c.Assert(err, IsNil)
  3129  	c.Check(updated, Equals, false)
  3130  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3131  }
  3132  
  3133  func (s *bootConfigSuite) TestBootConfigUpdateBootloaderFindErr(c *C) {
  3134  	coreDev := boottest.MockUC20Device("", nil)
  3135  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3136  
  3137  	bootloader.ForceError(errors.New("mocked find error"))
  3138  	defer bootloader.ForceError(nil)
  3139  
  3140  	m := &boot.Modeenv{
  3141  		Mode: "run",
  3142  	}
  3143  	c.Assert(m.WriteTo(""), IsNil)
  3144  
  3145  	updated, err := boot.UpdateManagedBootConfigs(coreDev)
  3146  	c.Assert(err, ErrorMatches, "internal error: cannot find trusted assets bootloader under .*: mocked find error")
  3147  	c.Check(updated, Equals, false)
  3148  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3149  }