github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/boot/boot_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2021 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  	model := coreDev.Model()
   882  
   883  	m := &boot.Modeenv{
   884  		Mode:           "run",
   885  		Base:           s.base1.Filename(),
   886  		CurrentKernels: []string{s.kern1.Filename()},
   887  		CurrentTrustedBootAssets: boot.BootAssetsMap{
   888  			"asset": {dataHash},
   889  		},
   890  		Model:          model.Model(),
   891  		BrandID:        model.BrandID(),
   892  		Grade:          string(model.Grade()),
   893  		ModelSignKeyID: model.SignKeyID(),
   894  	}
   895  
   896  	r := setupUC20Bootenv(
   897  		c,
   898  		tab.MockBootloader,
   899  		&bootenv20Setup{
   900  			modeenv:    m,
   901  			kern:       s.kern1,
   902  			kernStatus: boot.DefaultStatus,
   903  		},
   904  	)
   905  	defer r()
   906  
   907  	resealCalls := 0
   908  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
   909  		resealCalls++
   910  
   911  		c.Assert(params.ModelParams, HasLen, 1)
   912  		mp := params.ModelParams[0]
   913  		c.Check(mp.Model.Model(), Equals, model.Model())
   914  		for _, ch := range mp.EFILoadChains {
   915  			printChain(c, ch, "-")
   916  		}
   917  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
   918  			secboot.NewLoadChain(assetBf,
   919  				secboot.NewLoadChain(runKernelBf)),
   920  			secboot.NewLoadChain(assetBf,
   921  				// TODO:UC20: once mock trusted assets
   922  				// bootloader can generated boot files for the
   923  				// kernel this will use candidate kernel
   924  				secboot.NewLoadChain(runKernelBf)),
   925  		})
   926  		// actual paths are seen only here
   927  		c.Check(tab.BootChainKernelPath, DeepEquals, []string{
   928  			s.kern1.MountFile(),
   929  			s.kern2.MountFile(),
   930  		})
   931  		return nil
   932  	})
   933  	defer restore()
   934  
   935  	// get the boot kernel participant from our new kernel snap
   936  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
   937  	// make sure it's not a trivial boot participant
   938  	c.Assert(bootKern.IsTrivial(), Equals, false)
   939  
   940  	// make the kernel used on next boot
   941  	rebootRequired, err := bootKern.SetNextBoot()
   942  	c.Assert(err, IsNil)
   943  	c.Assert(rebootRequired, Equals, true)
   944  
   945  	// make sure the env was updated
   946  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
   947  	c.Assert(err, IsNil)
   948  	c.Assert(bvars, DeepEquals, map[string]string{
   949  		"kernel_status":   boot.TryStatus,
   950  		"snap_kernel":     s.kern1.Filename(),
   951  		"snap_try_kernel": s.kern2.Filename(),
   952  	})
   953  
   954  	// and that the modeenv now has this kernel listed
   955  	m2, err := boot.ReadModeenv("")
   956  	c.Assert(err, IsNil)
   957  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
   958  
   959  	c.Check(resealCalls, Equals, 1)
   960  }
   961  
   962  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewUnassertedKernelSnapWithReseal(c *C) {
   963  	// checked by resealKeyToModeenv
   964  	s.stampSealedKeys(c, dirs.GlobalRootDir)
   965  
   966  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
   967  
   968  	data := []byte("foobar")
   969  	// SHA3-384
   970  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
   971  
   972  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
   973  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
   974  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
   975  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
   976  
   977  	// mock the files in cache
   978  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
   979  		"asset-" + dataHash,
   980  	})
   981  
   982  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
   983  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
   984  
   985  	tab.BootChainList = []bootloader.BootFile{
   986  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
   987  		// TODO:UC20: fix mocked trusted assets bootloader to actually
   988  		// geenerate kernel boot files
   989  		runKernelBf,
   990  	}
   991  
   992  	uc20Model := boottest.MakeMockUC20Model()
   993  	coreDev := boottest.MockUC20Device("", uc20Model)
   994  	c.Assert(coreDev.HasModeenv(), Equals, true)
   995  
   996  	m := &boot.Modeenv{
   997  		Mode:           "run",
   998  		Base:           s.base1.Filename(),
   999  		CurrentKernels: []string{s.ukern1.Filename()},
  1000  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1001  			"asset": {dataHash},
  1002  		},
  1003  		Model:          uc20Model.Model(),
  1004  		BrandID:        uc20Model.BrandID(),
  1005  		Grade:          string(uc20Model.Grade()),
  1006  		ModelSignKeyID: uc20Model.SignKeyID(),
  1007  	}
  1008  
  1009  	r := setupUC20Bootenv(
  1010  		c,
  1011  		tab.MockBootloader,
  1012  		&bootenv20Setup{
  1013  			modeenv:    m,
  1014  			kern:       s.ukern1,
  1015  			kernStatus: boot.DefaultStatus,
  1016  		},
  1017  	)
  1018  	defer r()
  1019  
  1020  	resealCalls := 0
  1021  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1022  		resealCalls++
  1023  
  1024  		c.Assert(params.ModelParams, HasLen, 1)
  1025  		mp := params.ModelParams[0]
  1026  		c.Check(mp.Model.Model(), Equals, uc20Model.Model())
  1027  		for _, ch := range mp.EFILoadChains {
  1028  			printChain(c, ch, "-")
  1029  		}
  1030  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  1031  			secboot.NewLoadChain(assetBf,
  1032  				secboot.NewLoadChain(runKernelBf)),
  1033  			secboot.NewLoadChain(assetBf,
  1034  				// TODO:UC20: once mock trusted assets
  1035  				// bootloader can generated boot files for the
  1036  				// kernel this will use candidate kernel
  1037  				secboot.NewLoadChain(runKernelBf)),
  1038  		})
  1039  		// actual paths are seen only here
  1040  		c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1041  			s.ukern1.MountFile(),
  1042  			s.ukern2.MountFile(),
  1043  		})
  1044  		return nil
  1045  	})
  1046  	defer restore()
  1047  
  1048  	// get the boot kernel participant from our new kernel snap
  1049  	bootKern := boot.Participant(s.ukern2, snap.TypeKernel, coreDev)
  1050  	// make sure it's not a trivial boot participant
  1051  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1052  
  1053  	// make the kernel used on next boot
  1054  	rebootRequired, err := bootKern.SetNextBoot()
  1055  	c.Assert(err, IsNil)
  1056  	c.Assert(rebootRequired, Equals, true)
  1057  
  1058  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1059  	c.Assert(err, IsNil)
  1060  	c.Assert(bvars, DeepEquals, map[string]string{
  1061  		"kernel_status":   boot.TryStatus,
  1062  		"snap_kernel":     s.ukern1.Filename(),
  1063  		"snap_try_kernel": s.ukern2.Filename(),
  1064  	})
  1065  
  1066  	// and that the modeenv now has this kernel listed
  1067  	m2, err := boot.ReadModeenv("")
  1068  	c.Assert(err, IsNil)
  1069  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename(), s.ukern2.Filename()})
  1070  
  1071  	c.Check(resealCalls, Equals, 1)
  1072  }
  1073  
  1074  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameKernelSnapNoReseal(c *C) {
  1075  	// checked by resealKeyToModeenv
  1076  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1077  
  1078  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1079  
  1080  	data := []byte("foobar")
  1081  	// SHA3-384
  1082  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1083  
  1084  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1085  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1086  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1087  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1088  
  1089  	// mock the files in cache
  1090  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1091  		"asset-" + dataHash,
  1092  	})
  1093  
  1094  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1095  
  1096  	tab.BootChainList = []bootloader.BootFile{
  1097  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1098  		runKernelBf,
  1099  	}
  1100  
  1101  	uc20Model := boottest.MakeMockUC20Model()
  1102  	coreDev := boottest.MockUC20Device("", uc20Model)
  1103  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1104  
  1105  	m := &boot.Modeenv{
  1106  		Mode:           "run",
  1107  		Base:           s.base1.Filename(),
  1108  		CurrentKernels: []string{s.kern1.Filename()},
  1109  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1110  			"asset": {dataHash},
  1111  		},
  1112  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  1113  
  1114  		Model:          uc20Model.Model(),
  1115  		BrandID:        uc20Model.BrandID(),
  1116  		Grade:          string(uc20Model.Grade()),
  1117  		ModelSignKeyID: uc20Model.SignKeyID(),
  1118  	}
  1119  
  1120  	r := setupUC20Bootenv(
  1121  		c,
  1122  		tab.MockBootloader,
  1123  		&bootenv20Setup{
  1124  			modeenv:    m,
  1125  			kern:       s.kern1,
  1126  			kernStatus: boot.DefaultStatus,
  1127  		},
  1128  	)
  1129  	defer r()
  1130  
  1131  	resealCalls := 0
  1132  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1133  		resealCalls++
  1134  		return fmt.Errorf("unexpected call")
  1135  	})
  1136  	defer restore()
  1137  
  1138  	// get the boot kernel participant from our kernel snap
  1139  	bootKern := boot.Participant(s.kern1, snap.TypeKernel, coreDev)
  1140  	// make sure it's not a trivial boot participant
  1141  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1142  
  1143  	// write boot-chains for current state that will stay unchanged
  1144  	bootChains := []boot.BootChain{{
  1145  		BrandID:        "my-brand",
  1146  		Model:          "my-model-uc20",
  1147  		Grade:          "dangerous",
  1148  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1149  		AssetChain: []boot.BootAsset{
  1150  			{
  1151  				Role: bootloader.RoleRunMode,
  1152  				Name: "asset",
  1153  				Hashes: []string{
  1154  					"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1155  				},
  1156  			},
  1157  		},
  1158  		Kernel:         "pc-kernel",
  1159  		KernelRevision: "1",
  1160  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1161  	}}
  1162  	err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1163  	c.Assert(err, IsNil)
  1164  
  1165  	// make the kernel used on next boot
  1166  	rebootRequired, err := bootKern.SetNextBoot()
  1167  	c.Assert(err, IsNil)
  1168  	c.Assert(rebootRequired, Equals, false)
  1169  
  1170  	// make sure the env is as expected
  1171  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1172  	c.Assert(err, IsNil)
  1173  	c.Assert(bvars, DeepEquals, map[string]string{
  1174  		"kernel_status":   boot.DefaultStatus,
  1175  		"snap_kernel":     s.kern1.Filename(),
  1176  		"snap_try_kernel": "",
  1177  	})
  1178  
  1179  	// and that the modeenv now has the one kernel listed
  1180  	m2, err := boot.ReadModeenv("")
  1181  	c.Assert(err, IsNil)
  1182  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1183  
  1184  	// boot chains were built
  1185  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1186  		s.kern1.MountFile(),
  1187  	})
  1188  	// no actual reseal
  1189  	c.Check(resealCalls, Equals, 0)
  1190  }
  1191  
  1192  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameUnassertedKernelSnapNoReseal(c *C) {
  1193  	// checked by resealKeyToModeenv
  1194  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1195  
  1196  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1197  
  1198  	data := []byte("foobar")
  1199  	// SHA3-384
  1200  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1201  
  1202  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1203  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1204  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1205  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1206  
  1207  	// mock the files in cache
  1208  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1209  		"asset-" + dataHash,
  1210  	})
  1211  
  1212  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1213  
  1214  	tab.BootChainList = []bootloader.BootFile{
  1215  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1216  		runKernelBf,
  1217  	}
  1218  
  1219  	uc20Model := boottest.MakeMockUC20Model()
  1220  	coreDev := boottest.MockUC20Device("", uc20Model)
  1221  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1222  
  1223  	m := &boot.Modeenv{
  1224  		Mode:           "run",
  1225  		Base:           s.base1.Filename(),
  1226  		CurrentKernels: []string{s.ukern1.Filename()},
  1227  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1228  			"asset": {dataHash},
  1229  		},
  1230  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  1231  
  1232  		Model:          uc20Model.Model(),
  1233  		BrandID:        uc20Model.BrandID(),
  1234  		Grade:          string(uc20Model.Grade()),
  1235  		ModelSignKeyID: uc20Model.SignKeyID(),
  1236  	}
  1237  
  1238  	r := setupUC20Bootenv(
  1239  		c,
  1240  		tab.MockBootloader,
  1241  		&bootenv20Setup{
  1242  			modeenv:    m,
  1243  			kern:       s.ukern1,
  1244  			kernStatus: boot.DefaultStatus,
  1245  		},
  1246  	)
  1247  	defer r()
  1248  
  1249  	resealCalls := 0
  1250  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1251  		resealCalls++
  1252  		return fmt.Errorf("unexpected call")
  1253  	})
  1254  	defer restore()
  1255  
  1256  	// get the boot kernel participant from our kernel snap
  1257  	bootKern := boot.Participant(s.ukern1, snap.TypeKernel, coreDev)
  1258  	// make sure it's not a trivial boot participant
  1259  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1260  
  1261  	// write boot-chains for current state that will stay unchanged
  1262  	bootChains := []boot.BootChain{{
  1263  		BrandID:        "my-brand",
  1264  		Model:          "my-model-uc20",
  1265  		Grade:          "dangerous",
  1266  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  1267  		AssetChain: []boot.BootAsset{
  1268  			{
  1269  				Role: bootloader.RoleRunMode,
  1270  				Name: "asset",
  1271  				Hashes: []string{
  1272  					"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  1273  				},
  1274  			},
  1275  		},
  1276  		Kernel:         "pc-kernel",
  1277  		KernelRevision: "",
  1278  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  1279  	}}
  1280  	err := boot.WriteBootChains(bootChains, filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  1281  	c.Assert(err, IsNil)
  1282  
  1283  	// make the kernel used on next boot
  1284  	rebootRequired, err := bootKern.SetNextBoot()
  1285  	c.Assert(err, IsNil)
  1286  	c.Assert(rebootRequired, Equals, false)
  1287  
  1288  	// make sure the env is as expected
  1289  	bvars, err := tab.GetBootVars("kernel_status", "snap_kernel", "snap_try_kernel")
  1290  	c.Assert(err, IsNil)
  1291  	c.Assert(bvars, DeepEquals, map[string]string{
  1292  		"kernel_status":   boot.DefaultStatus,
  1293  		"snap_kernel":     s.ukern1.Filename(),
  1294  		"snap_try_kernel": "",
  1295  	})
  1296  
  1297  	// and that the modeenv now has the one kernel listed
  1298  	m2, err := boot.ReadModeenv("")
  1299  	c.Assert(err, IsNil)
  1300  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.ukern1.Filename()})
  1301  
  1302  	// boot chains were built
  1303  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  1304  		s.ukern1.MountFile(),
  1305  	})
  1306  	// no actual reseal
  1307  	c.Check(resealCalls, Equals, 0)
  1308  }
  1309  
  1310  func (s *bootenv20EnvRefKernelSuite) TestCoreParticipant20SetNextNewKernelSnap(c *C) {
  1311  	coreDev := boottest.MockUC20Device("", nil)
  1312  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1313  
  1314  	r := setupUC20Bootenv(
  1315  		c,
  1316  		s.bootloader,
  1317  		s.normalDefaultState,
  1318  	)
  1319  	defer r()
  1320  
  1321  	// get the boot kernel participant from our new kernel snap
  1322  	bootKern := boot.Participant(s.kern2, snap.TypeKernel, coreDev)
  1323  	// make sure it's not a trivial boot participant
  1324  	c.Assert(bootKern.IsTrivial(), Equals, false)
  1325  
  1326  	// make the kernel used on next boot
  1327  	rebootRequired, err := bootKern.SetNextBoot()
  1328  	c.Assert(err, IsNil)
  1329  	c.Assert(rebootRequired, Equals, true)
  1330  
  1331  	// make sure the env was updated
  1332  	m := s.bootloader.BootVars
  1333  	c.Assert(m, DeepEquals, map[string]string{
  1334  		"kernel_status":   boot.TryStatus,
  1335  		"snap_kernel":     s.kern1.Filename(),
  1336  		"snap_try_kernel": s.kern2.Filename(),
  1337  	})
  1338  
  1339  	// and that the modeenv now has this kernel listed
  1340  	m2, err := boot.ReadModeenv("")
  1341  	c.Assert(err, IsNil)
  1342  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename(), s.kern2.Filename()})
  1343  }
  1344  
  1345  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) {
  1346  	coreDev := boottest.MockUC20Device("", nil)
  1347  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1348  
  1349  	// set all the same vars as if we were doing trying, except don't set a try
  1350  	// kernel
  1351  	r := setupUC20Bootenv(
  1352  		c,
  1353  		s.bootloader,
  1354  		&bootenv20Setup{
  1355  			modeenv: &boot.Modeenv{
  1356  				Mode:           "run",
  1357  				Base:           s.base1.Filename(),
  1358  				CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1359  			},
  1360  			kern: s.kern1,
  1361  			// no try-kernel
  1362  			kernStatus: boot.TryingStatus,
  1363  		},
  1364  	)
  1365  	defer r()
  1366  
  1367  	// mark successful
  1368  	err := boot.MarkBootSuccessful(coreDev)
  1369  	c.Assert(err, IsNil)
  1370  
  1371  	// check that the bootloader variable was cleaned
  1372  	expected := map[string]string{"kernel_status": boot.DefaultStatus}
  1373  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1374  
  1375  	// check that MarkBootSuccessful didn't enable a kernel (since there was no
  1376  	// try kernel)
  1377  	_, nEnableCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1378  	c.Assert(nEnableCalls, Equals, 0)
  1379  
  1380  	// we will always end up disabling a try-kernel though as cleanup
  1381  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1382  	c.Assert(nDisableTryCalls, Equals, 1)
  1383  
  1384  	// do it again, verify it's still okay
  1385  	err = boot.MarkBootSuccessful(coreDev)
  1386  	c.Assert(err, IsNil)
  1387  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1388  
  1389  	// no new enabled kernels
  1390  	_, nEnableCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1391  	c.Assert(nEnableCalls, Equals, 0)
  1392  
  1393  	// again we will try to cleanup any leftover try-kernels
  1394  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1395  	c.Assert(nDisableTryCalls, Equals, 2)
  1396  
  1397  	// check that the modeenv re-wrote the CurrentKernels
  1398  	m2, err := boot.ReadModeenv("")
  1399  	c.Assert(err, IsNil)
  1400  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1401  }
  1402  
  1403  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelStatusTryingNoKernelSnapCleansUp(c *C) {
  1404  	coreDev := boottest.MockUC20Device("", nil)
  1405  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1406  
  1407  	// set all the same vars as if we were doing trying, except don't set a try
  1408  	// kernel
  1409  	r := setupUC20Bootenv(
  1410  		c,
  1411  		s.bootloader,
  1412  		&bootenv20Setup{
  1413  			modeenv: &boot.Modeenv{
  1414  				Mode:           "run",
  1415  				Base:           s.base1.Filename(),
  1416  				CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1417  			},
  1418  			kern: s.kern1,
  1419  			// no try-kernel
  1420  			kernStatus: boot.TryingStatus,
  1421  		},
  1422  	)
  1423  	defer r()
  1424  
  1425  	// mark successful
  1426  	err := boot.MarkBootSuccessful(coreDev)
  1427  	c.Assert(err, IsNil)
  1428  
  1429  	// make sure the env was updated
  1430  	expected := map[string]string{
  1431  		"kernel_status":   boot.DefaultStatus,
  1432  		"snap_kernel":     s.kern1.Filename(),
  1433  		"snap_try_kernel": "",
  1434  	}
  1435  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1436  
  1437  	// do it again, verify it's still okay
  1438  	err = boot.MarkBootSuccessful(coreDev)
  1439  	c.Assert(err, IsNil)
  1440  
  1441  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1442  
  1443  	// check that the modeenv re-wrote the CurrentKernels
  1444  	m2, err := boot.ReadModeenv("")
  1445  	c.Assert(err, IsNil)
  1446  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern1.Filename()})
  1447  }
  1448  
  1449  func (s *bootenv20Suite) TestMarkBootSuccessful20BaseStatusTryingNoTryBaseSnapCleansUp(c *C) {
  1450  	m := &boot.Modeenv{
  1451  		Mode: "run",
  1452  		Base: s.base1.Filename(),
  1453  		// no TryBase set
  1454  		BaseStatus: boot.TryingStatus,
  1455  	}
  1456  	r := setupUC20Bootenv(
  1457  		c,
  1458  		s.bootloader,
  1459  		&bootenv20Setup{
  1460  			modeenv: m,
  1461  			// no kernel setup necessary
  1462  		},
  1463  	)
  1464  	defer r()
  1465  
  1466  	coreDev := boottest.MockUC20Device("", nil)
  1467  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1468  
  1469  	// mark successful
  1470  	err := boot.MarkBootSuccessful(coreDev)
  1471  	c.Assert(err, IsNil)
  1472  
  1473  	// check that the modeenv base_status was re-written to default
  1474  	m2, err := boot.ReadModeenv("")
  1475  	c.Assert(err, IsNil)
  1476  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1477  	c.Assert(m2.Base, Equals, m.Base)
  1478  	c.Assert(m2.TryBase, Equals, m.TryBase)
  1479  
  1480  	// do it again, verify it's still okay
  1481  	err = boot.MarkBootSuccessful(coreDev)
  1482  	c.Assert(err, IsNil)
  1483  
  1484  	m3, err := boot.ReadModeenv("")
  1485  	c.Assert(err, IsNil)
  1486  	c.Assert(m3.BaseStatus, Equals, boot.DefaultStatus)
  1487  	c.Assert(m3.Base, Equals, m.Base)
  1488  	c.Assert(m3.TryBase, Equals, m.TryBase)
  1489  }
  1490  
  1491  func (s *bootenv20Suite) TestCoreParticipant20SetNextSameBaseSnap(c *C) {
  1492  	coreDev := boottest.MockUC20Device("", nil)
  1493  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1494  
  1495  	m := &boot.Modeenv{
  1496  		Mode: "run",
  1497  		Base: s.base1.Filename(),
  1498  	}
  1499  	r := setupUC20Bootenv(
  1500  		c,
  1501  		s.bootloader,
  1502  		&bootenv20Setup{
  1503  			modeenv: m,
  1504  			// no kernel setup necessary
  1505  		},
  1506  	)
  1507  	defer r()
  1508  
  1509  	// get the boot base participant from our base snap
  1510  	bootBase := boot.Participant(s.base1, snap.TypeBase, coreDev)
  1511  	// make sure it's not a trivial boot participant
  1512  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1513  
  1514  	// make the base used on next boot
  1515  	rebootRequired, err := bootBase.SetNextBoot()
  1516  	c.Assert(err, IsNil)
  1517  
  1518  	// we don't need to reboot because it's the same base snap
  1519  	c.Assert(rebootRequired, Equals, false)
  1520  
  1521  	// make sure the modeenv wasn't changed
  1522  	m2, err := boot.ReadModeenv("")
  1523  	c.Assert(err, IsNil)
  1524  	c.Assert(m2.Base, Equals, m.Base)
  1525  	c.Assert(m2.BaseStatus, Equals, m.BaseStatus)
  1526  	c.Assert(m2.TryBase, Equals, m.TryBase)
  1527  }
  1528  
  1529  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnap(c *C) {
  1530  	coreDev := boottest.MockUC20Device("", nil)
  1531  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1532  
  1533  	// default state
  1534  	m := &boot.Modeenv{
  1535  		Mode: "run",
  1536  		Base: s.base1.Filename(),
  1537  	}
  1538  	r := setupUC20Bootenv(
  1539  		c,
  1540  		s.bootloader,
  1541  		&bootenv20Setup{
  1542  			modeenv: m,
  1543  			// no kernel setup necessary
  1544  		},
  1545  	)
  1546  	defer r()
  1547  
  1548  	// get the boot base participant from our new base snap
  1549  	bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev)
  1550  	// make sure it's not a trivial boot participant
  1551  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1552  
  1553  	// make the base used on next boot
  1554  	rebootRequired, err := bootBase.SetNextBoot()
  1555  	c.Assert(err, IsNil)
  1556  	c.Assert(rebootRequired, Equals, true)
  1557  
  1558  	// make sure the modeenv was updated
  1559  	m2, err := boot.ReadModeenv("")
  1560  	c.Assert(err, IsNil)
  1561  	c.Assert(m2.Base, Equals, m.Base)
  1562  	c.Assert(m2.BaseStatus, Equals, boot.TryStatus)
  1563  	c.Assert(m2.TryBase, Equals, s.base2.Filename())
  1564  }
  1565  
  1566  func (s *bootenv20Suite) TestCoreParticipant20SetNextNewBaseSnapNoReseal(c *C) {
  1567  	// checked by resealKeyToModeenv
  1568  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1569  
  1570  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1571  
  1572  	coreDev := boottest.MockUC20Device("", nil)
  1573  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1574  
  1575  	resealCalls := 0
  1576  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1577  		resealCalls++
  1578  		return nil
  1579  	})
  1580  	defer restore()
  1581  
  1582  	// we should not even need to build boot chains
  1583  	tab.BootChainErr = errors.New("boom")
  1584  
  1585  	// default state
  1586  	m := &boot.Modeenv{
  1587  		Mode: "run",
  1588  		Base: s.base1.Filename(),
  1589  	}
  1590  	r := setupUC20Bootenv(
  1591  		c,
  1592  		tab.MockBootloader,
  1593  		&bootenv20Setup{
  1594  			modeenv: m,
  1595  			// no kernel setup necessary
  1596  		},
  1597  	)
  1598  	defer r()
  1599  
  1600  	// get the boot base participant from our new base snap
  1601  	bootBase := boot.Participant(s.base2, snap.TypeBase, coreDev)
  1602  	// make sure it's not a trivial boot participant
  1603  	c.Assert(bootBase.IsTrivial(), Equals, false)
  1604  
  1605  	// make the base used on next boot
  1606  	rebootRequired, err := bootBase.SetNextBoot()
  1607  	c.Assert(err, IsNil)
  1608  	c.Assert(rebootRequired, Equals, true)
  1609  
  1610  	// make sure the modeenv was updated
  1611  	m2, err := boot.ReadModeenv("")
  1612  	c.Assert(err, IsNil)
  1613  	c.Assert(m2.Base, Equals, m.Base)
  1614  	c.Assert(m2.BaseStatus, Equals, boot.TryStatus)
  1615  	c.Assert(m2.TryBase, Equals, s.base2.Filename())
  1616  
  1617  	// no reseal
  1618  	c.Check(resealCalls, Equals, 0)
  1619  }
  1620  
  1621  func (s *bootenvSuite) TestMarkBootSuccessfulAllSnap(c *C) {
  1622  	coreDev := boottest.MockDevice("some-snap")
  1623  
  1624  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1625  	s.bootloader.BootVars["snap_try_core"] = "os1"
  1626  	s.bootloader.BootVars["snap_try_kernel"] = "k1"
  1627  	err := boot.MarkBootSuccessful(coreDev)
  1628  	c.Assert(err, IsNil)
  1629  
  1630  	expected := map[string]string{
  1631  		// cleared
  1632  		"snap_mode":       boot.DefaultStatus,
  1633  		"snap_try_kernel": "",
  1634  		"snap_try_core":   "",
  1635  		// updated
  1636  		"snap_kernel": "k1",
  1637  		"snap_core":   "os1",
  1638  	}
  1639  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1640  
  1641  	// do it again, verify its still valid
  1642  	err = boot.MarkBootSuccessful(coreDev)
  1643  	c.Assert(err, IsNil)
  1644  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1645  }
  1646  
  1647  func (s *bootenv20Suite) TestMarkBootSuccessful20AllSnap(c *C) {
  1648  	coreDev := boottest.MockUC20Device("", nil)
  1649  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1650  
  1651  	// bonus points: we were trying both a base snap and a kernel snap
  1652  	m := &boot.Modeenv{
  1653  		Mode:           "run",
  1654  		Base:           s.base1.Filename(),
  1655  		TryBase:        s.base2.Filename(),
  1656  		BaseStatus:     boot.TryingStatus,
  1657  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1658  	}
  1659  	r := setupUC20Bootenv(
  1660  		c,
  1661  		s.bootloader,
  1662  		&bootenv20Setup{
  1663  			modeenv:    m,
  1664  			kern:       s.kern1,
  1665  			tryKern:    s.kern2,
  1666  			kernStatus: boot.TryingStatus,
  1667  		},
  1668  	)
  1669  	defer r()
  1670  
  1671  	err := boot.MarkBootSuccessful(coreDev)
  1672  	c.Assert(err, IsNil)
  1673  
  1674  	// check the bootloader variables
  1675  	expected := map[string]string{
  1676  		// cleared
  1677  		"kernel_status": boot.DefaultStatus,
  1678  	}
  1679  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1680  
  1681  	// check that we called EnableKernel() on the try-kernel
  1682  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1683  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1684  
  1685  	// and that we disabled a try kernel
  1686  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1687  	c.Assert(nDisableTryCalls, Equals, 1)
  1688  
  1689  	// also check that the modeenv was updated
  1690  	m2, err := boot.ReadModeenv("")
  1691  	c.Assert(err, IsNil)
  1692  	c.Assert(m2.Base, Equals, s.base2.Filename())
  1693  	c.Assert(m2.TryBase, Equals, "")
  1694  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1695  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1696  
  1697  	// do it again, verify its still valid
  1698  	err = boot.MarkBootSuccessful(coreDev)
  1699  	c.Assert(err, IsNil)
  1700  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1701  
  1702  	// no new enabled kernels
  1703  	actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1704  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1705  	// we always disable the try kernel as a cleanup operation, so there's one
  1706  	// more call here
  1707  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1708  	c.Assert(nDisableTryCalls, Equals, 2)
  1709  }
  1710  
  1711  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20AllSnap(c *C) {
  1712  	coreDev := boottest.MockUC20Device("", nil)
  1713  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1714  
  1715  	// bonus points: we were trying both a base snap and a kernel snap
  1716  	m := &boot.Modeenv{
  1717  		Mode:           "run",
  1718  		Base:           s.base1.Filename(),
  1719  		TryBase:        s.base2.Filename(),
  1720  		BaseStatus:     boot.TryingStatus,
  1721  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1722  	}
  1723  	r := setupUC20Bootenv(
  1724  		c,
  1725  		s.bootloader,
  1726  		&bootenv20Setup{
  1727  			modeenv:    m,
  1728  			kern:       s.kern1,
  1729  			tryKern:    s.kern2,
  1730  			kernStatus: boot.TryingStatus,
  1731  		},
  1732  	)
  1733  	defer r()
  1734  
  1735  	err := boot.MarkBootSuccessful(coreDev)
  1736  	c.Assert(err, IsNil)
  1737  
  1738  	// check the bootloader variables
  1739  	expected := map[string]string{
  1740  		// cleared
  1741  		"kernel_status":   boot.DefaultStatus,
  1742  		"snap_try_kernel": "",
  1743  		// enabled new kernel
  1744  		"snap_kernel": s.kern2.Filename(),
  1745  	}
  1746  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1747  
  1748  	// also check that the modeenv was updated
  1749  	m2, err := boot.ReadModeenv("")
  1750  	c.Assert(err, IsNil)
  1751  	c.Assert(m2.Base, Equals, s.base2.Filename())
  1752  	c.Assert(m2.TryBase, Equals, "")
  1753  	c.Assert(m2.BaseStatus, Equals, boot.DefaultStatus)
  1754  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1755  
  1756  	// do it again, verify its still valid
  1757  	err = boot.MarkBootSuccessful(coreDev)
  1758  	c.Assert(err, IsNil)
  1759  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1760  }
  1761  
  1762  func (s *bootenvSuite) TestMarkBootSuccessfulKernelUpdate(c *C) {
  1763  	coreDev := boottest.MockDevice("some-snap")
  1764  
  1765  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1766  	s.bootloader.BootVars["snap_core"] = "os1"
  1767  	s.bootloader.BootVars["snap_kernel"] = "k1"
  1768  	s.bootloader.BootVars["snap_try_core"] = ""
  1769  	s.bootloader.BootVars["snap_try_kernel"] = "k2"
  1770  	err := boot.MarkBootSuccessful(coreDev)
  1771  	c.Assert(err, IsNil)
  1772  	c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{
  1773  		// cleared
  1774  		"snap_mode":       boot.DefaultStatus,
  1775  		"snap_try_kernel": "",
  1776  		"snap_try_core":   "",
  1777  		// unchanged
  1778  		"snap_core": "os1",
  1779  		// updated
  1780  		"snap_kernel": "k2",
  1781  	})
  1782  }
  1783  
  1784  func (s *bootenvSuite) TestMarkBootSuccessfulBaseUpdate(c *C) {
  1785  	coreDev := boottest.MockDevice("some-snap")
  1786  
  1787  	s.bootloader.BootVars["snap_mode"] = boot.TryingStatus
  1788  	s.bootloader.BootVars["snap_core"] = "os1"
  1789  	s.bootloader.BootVars["snap_kernel"] = "k1"
  1790  	s.bootloader.BootVars["snap_try_core"] = "os2"
  1791  	s.bootloader.BootVars["snap_try_kernel"] = ""
  1792  	err := boot.MarkBootSuccessful(coreDev)
  1793  	c.Assert(err, IsNil)
  1794  	c.Assert(s.bootloader.BootVars, DeepEquals, map[string]string{
  1795  		// cleared
  1796  		"snap_mode":     boot.DefaultStatus,
  1797  		"snap_try_core": "",
  1798  		// unchanged
  1799  		"snap_kernel":     "k1",
  1800  		"snap_try_kernel": "",
  1801  		// updated
  1802  		"snap_core": "os2",
  1803  	})
  1804  }
  1805  
  1806  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdate(c *C) {
  1807  	// trying a kernel snap
  1808  	m := &boot.Modeenv{
  1809  		Mode:           "run",
  1810  		Base:           s.base1.Filename(),
  1811  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1812  	}
  1813  	r := setupUC20Bootenv(
  1814  		c,
  1815  		s.bootloader,
  1816  		&bootenv20Setup{
  1817  			modeenv:    m,
  1818  			kern:       s.kern1,
  1819  			tryKern:    s.kern2,
  1820  			kernStatus: boot.TryingStatus,
  1821  		},
  1822  	)
  1823  	defer r()
  1824  
  1825  	coreDev := boottest.MockUC20Device("", nil)
  1826  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1827  
  1828  	// mark successful
  1829  	err := boot.MarkBootSuccessful(coreDev)
  1830  	c.Assert(err, IsNil)
  1831  
  1832  	// check the bootloader variables
  1833  	expected := map[string]string{"kernel_status": boot.DefaultStatus}
  1834  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1835  
  1836  	// check that MarkBootSuccessful enabled the try kernel
  1837  	actual, _ := s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1838  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1839  
  1840  	// and that we disabled a try kernel
  1841  	_, nDisableTryCalls := s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1842  	c.Assert(nDisableTryCalls, Equals, 1)
  1843  
  1844  	// check that the new kernel is the only one in modeenv
  1845  	m2, err := boot.ReadModeenv("")
  1846  	c.Assert(err, IsNil)
  1847  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1848  
  1849  	// do it again, verify its still valid
  1850  	err = boot.MarkBootSuccessful(coreDev)
  1851  	c.Assert(err, IsNil)
  1852  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1853  
  1854  	// no new bootloader calls
  1855  	actual, _ = s.bootloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
  1856  	c.Assert(actual, DeepEquals, []snap.PlaceInfo{s.kern2})
  1857  
  1858  	// we did disable the kernel again because we always do this to cleanup in
  1859  	// case there were leftovers
  1860  	_, nDisableTryCalls = s.bootloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
  1861  	c.Assert(nDisableTryCalls, Equals, 2)
  1862  }
  1863  
  1864  func (s *bootenv20Suite) TestMarkBootSuccessful20KernelUpdateWithReseal(c *C) {
  1865  	// checked by resealKeyToModeenv
  1866  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  1867  
  1868  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  1869  
  1870  	data := []byte("foobar")
  1871  	// SHA3-384
  1872  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  1873  
  1874  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  1875  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  1876  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  1877  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  1878  
  1879  	// mock the files in cache
  1880  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  1881  		"asset-" + dataHash,
  1882  	})
  1883  
  1884  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRunMode)
  1885  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  1886  
  1887  	tab.BootChainList = []bootloader.BootFile{
  1888  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  1889  		runKernelBf,
  1890  	}
  1891  
  1892  	coreDev := boottest.MockUC20Device("", nil)
  1893  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1894  	model := coreDev.Model()
  1895  
  1896  	// trying a kernel snap
  1897  	m := &boot.Modeenv{
  1898  		Mode:           "run",
  1899  		Base:           s.base1.Filename(),
  1900  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1901  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  1902  			"asset": {dataHash},
  1903  		},
  1904  		Model:          model.Model(),
  1905  		BrandID:        model.BrandID(),
  1906  		Grade:          string(model.Grade()),
  1907  		ModelSignKeyID: model.SignKeyID(),
  1908  	}
  1909  	r := setupUC20Bootenv(
  1910  		c,
  1911  		tab.MockBootloader,
  1912  		&bootenv20Setup{
  1913  			modeenv:    m,
  1914  			kern:       s.kern1,
  1915  			tryKern:    s.kern2,
  1916  			kernStatus: boot.TryingStatus,
  1917  		},
  1918  	)
  1919  	defer r()
  1920  
  1921  	resealCalls := 0
  1922  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  1923  		resealCalls++
  1924  
  1925  		c.Assert(params.ModelParams, HasLen, 1)
  1926  		mp := params.ModelParams[0]
  1927  		c.Check(mp.Model.Model(), Equals, model.Model())
  1928  		for _, ch := range mp.EFILoadChains {
  1929  			printChain(c, ch, "-")
  1930  		}
  1931  		c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  1932  			secboot.NewLoadChain(assetBf,
  1933  				secboot.NewLoadChain(runKernelBf)),
  1934  		})
  1935  		return nil
  1936  	})
  1937  	defer restore()
  1938  
  1939  	// mark successful
  1940  	err := boot.MarkBootSuccessful(coreDev)
  1941  	c.Assert(err, IsNil)
  1942  
  1943  	// check the bootloader variables
  1944  	expected := map[string]string{
  1945  		"kernel_status":   boot.DefaultStatus,
  1946  		"snap_kernel":     s.kern2.Filename(),
  1947  		"snap_try_kernel": boot.DefaultStatus,
  1948  	}
  1949  	c.Assert(tab.BootVars, DeepEquals, expected)
  1950  
  1951  	// check that the new kernel is the only one in modeenv
  1952  	m2, err := boot.ReadModeenv("")
  1953  	c.Assert(err, IsNil)
  1954  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1955  
  1956  	c.Check(resealCalls, Equals, 1)
  1957  }
  1958  
  1959  func (s *bootenv20EnvRefKernelSuite) TestMarkBootSuccessful20KernelUpdate(c *C) {
  1960  	// trying a kernel snap
  1961  	m := &boot.Modeenv{
  1962  		Mode:           "run",
  1963  		Base:           s.base1.Filename(),
  1964  		CurrentKernels: []string{s.kern1.Filename(), s.kern2.Filename()},
  1965  	}
  1966  	r := setupUC20Bootenv(
  1967  		c,
  1968  		s.bootloader,
  1969  		&bootenv20Setup{
  1970  			modeenv:    m,
  1971  			kern:       s.kern1,
  1972  			tryKern:    s.kern2,
  1973  			kernStatus: boot.TryingStatus,
  1974  		},
  1975  	)
  1976  	defer r()
  1977  
  1978  	coreDev := boottest.MockUC20Device("", nil)
  1979  	c.Assert(coreDev.HasModeenv(), Equals, true)
  1980  
  1981  	// mark successful
  1982  	err := boot.MarkBootSuccessful(coreDev)
  1983  	c.Assert(err, IsNil)
  1984  
  1985  	// check the bootloader variables
  1986  	expected := map[string]string{
  1987  		"kernel_status":   boot.DefaultStatus,
  1988  		"snap_kernel":     s.kern2.Filename(),
  1989  		"snap_try_kernel": "",
  1990  	}
  1991  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  1992  
  1993  	// check that the new kernel is the only one in modeenv
  1994  	m2, err := boot.ReadModeenv("")
  1995  	c.Assert(err, IsNil)
  1996  	c.Assert(m2.CurrentKernels, DeepEquals, []string{s.kern2.Filename()})
  1997  
  1998  	// do it again, verify its still valid
  1999  	err = boot.MarkBootSuccessful(coreDev)
  2000  	c.Assert(err, IsNil)
  2001  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  2002  	c.Assert(s.bootloader.BootVars, DeepEquals, expected)
  2003  }
  2004  
  2005  func (s *bootenv20Suite) TestMarkBootSuccessful20BaseUpdate(c *C) {
  2006  	// we were trying a base snap
  2007  	m := &boot.Modeenv{
  2008  		Mode:           "run",
  2009  		Base:           s.base1.Filename(),
  2010  		TryBase:        s.base2.Filename(),
  2011  		BaseStatus:     boot.TryingStatus,
  2012  		CurrentKernels: []string{s.kern1.Filename()},
  2013  	}
  2014  	r := setupUC20Bootenv(
  2015  		c,
  2016  		s.bootloader,
  2017  		&bootenv20Setup{
  2018  			modeenv:    m,
  2019  			kern:       s.kern1,
  2020  			kernStatus: boot.DefaultStatus,
  2021  		},
  2022  	)
  2023  	defer r()
  2024  
  2025  	coreDev := boottest.MockUC20Device("", nil)
  2026  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2027  
  2028  	// mark successful
  2029  	err := boot.MarkBootSuccessful(coreDev)
  2030  	c.Assert(err, IsNil)
  2031  
  2032  	// check the modeenv
  2033  	m2, err := boot.ReadModeenv("")
  2034  	c.Assert(err, IsNil)
  2035  	c.Assert(m2.Base, Equals, s.base2.Filename())
  2036  	c.Assert(m2.TryBase, Equals, "")
  2037  	c.Assert(m2.BaseStatus, Equals, "")
  2038  
  2039  	// do it again, verify its still valid
  2040  	err = boot.MarkBootSuccessful(coreDev)
  2041  	c.Assert(err, IsNil)
  2042  
  2043  	// check the modeenv again
  2044  	m3, err := boot.ReadModeenv("")
  2045  	c.Assert(err, IsNil)
  2046  	c.Assert(m3.Base, Equals, s.base2.Filename())
  2047  	c.Assert(m3.TryBase, Equals, "")
  2048  	c.Assert(m3.BaseStatus, Equals, "")
  2049  }
  2050  
  2051  func (s *bootenv20Suite) bootloaderWithTrustedAssets(c *C, trustedAssets []string) *bootloadertest.MockTrustedAssetsBootloader {
  2052  	// TODO:UC20: this should be an ExtractedRecoveryKernelImageBootloader
  2053  	// because that would reflect our main currently supported
  2054  	// trusted assets bootloader (grub)
  2055  	tab := bootloadertest.Mock("trusted", "").WithTrustedAssets()
  2056  	bootloader.Force(tab)
  2057  	tab.TrustedAssetsList = trustedAssets
  2058  	s.AddCleanup(func() { bootloader.Force(nil) })
  2059  	return tab
  2060  }
  2061  
  2062  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateHappy(c *C) {
  2063  	// checked by resealKeyToModeenv
  2064  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2065  
  2066  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset", "shim"})
  2067  
  2068  	data := []byte("foobar")
  2069  	// SHA3-384
  2070  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2071  	shim := []byte("shim")
  2072  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2073  
  2074  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755), IsNil)
  2075  	c.Assert(os.MkdirAll(boot.InitramfsUbuntuSeedDir, 0755), IsNil)
  2076  	// only asset for ubuntu
  2077  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  2078  	// shim and asset for seed
  2079  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  2080  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2081  
  2082  	// mock the files in cache
  2083  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2084  		"shim-recoveryshimhash",
  2085  		"shim-" + shimHash,
  2086  		"asset-assethash",
  2087  		"asset-recoveryassethash",
  2088  		"asset-" + dataHash,
  2089  	})
  2090  
  2091  	shimBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("shim-%s", shimHash)), bootloader.RoleRecovery)
  2092  	assetBf := bootloader.NewBootFile("", filepath.Join(dirs.SnapBootAssetsDir, "trusted", fmt.Sprintf("asset-%s", dataHash)), bootloader.RoleRecovery)
  2093  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2094  	recoveryKernelBf := bootloader.NewBootFile("pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2095  
  2096  	tab.BootChainList = []bootloader.BootFile{
  2097  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2098  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2099  		runKernelBf,
  2100  	}
  2101  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2102  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2103  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2104  		recoveryKernelBf,
  2105  	}
  2106  
  2107  	uc20Model := boottest.MakeMockUC20Model()
  2108  
  2109  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2110  		return uc20Model, []*seed.Snap{mockKernelSeedSnap(c, snap.R(1)), mockGadgetSeedSnap(c, nil)}, nil
  2111  	})
  2112  	defer restore()
  2113  
  2114  	// we were trying an update of boot assets
  2115  	m := &boot.Modeenv{
  2116  		Mode:           "run",
  2117  		Base:           s.base1.Filename(),
  2118  		CurrentKernels: []string{s.kern1.Filename()},
  2119  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2120  			"asset": {"assethash", dataHash},
  2121  		},
  2122  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2123  			"asset": {"recoveryassethash", dataHash},
  2124  			"shim":  {"recoveryshimhash", shimHash},
  2125  		},
  2126  		CurrentRecoverySystems: []string{"system"},
  2127  
  2128  		Model:          uc20Model.Model(),
  2129  		BrandID:        uc20Model.BrandID(),
  2130  		Grade:          string(uc20Model.Grade()),
  2131  		ModelSignKeyID: uc20Model.SignKeyID(),
  2132  	}
  2133  	r := setupUC20Bootenv(
  2134  		c,
  2135  		tab.MockBootloader,
  2136  		&bootenv20Setup{
  2137  			modeenv:    m,
  2138  			kern:       s.kern1,
  2139  			kernStatus: boot.DefaultStatus,
  2140  		},
  2141  	)
  2142  	defer r()
  2143  
  2144  	coreDev := boottest.MockUC20Device("", uc20Model)
  2145  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2146  
  2147  	resealCalls := 0
  2148  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2149  		resealCalls++
  2150  
  2151  		c.Assert(params.ModelParams, HasLen, 1)
  2152  		mp := params.ModelParams[0]
  2153  		c.Check(mp.Model.Model(), Equals, uc20Model.Model())
  2154  		for _, ch := range mp.EFILoadChains {
  2155  			printChain(c, ch, "-")
  2156  		}
  2157  		switch resealCalls {
  2158  		case 1:
  2159  			c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2160  				secboot.NewLoadChain(shimBf,
  2161  					secboot.NewLoadChain(assetBf,
  2162  						secboot.NewLoadChain(recoveryKernelBf))),
  2163  				secboot.NewLoadChain(shimBf,
  2164  					secboot.NewLoadChain(assetBf,
  2165  						secboot.NewLoadChain(runKernelBf))),
  2166  			})
  2167  		case 2:
  2168  			c.Check(mp.EFILoadChains, DeepEquals, []*secboot.LoadChain{
  2169  				secboot.NewLoadChain(shimBf,
  2170  					secboot.NewLoadChain(assetBf,
  2171  						secboot.NewLoadChain(recoveryKernelBf))),
  2172  			})
  2173  		default:
  2174  			c.Errorf("unexpected additional call to secboot.ResealKey (call # %d)", resealCalls)
  2175  		}
  2176  		return nil
  2177  	})
  2178  	defer restore()
  2179  
  2180  	// mark successful
  2181  	err := boot.MarkBootSuccessful(coreDev)
  2182  	c.Assert(err, IsNil)
  2183  
  2184  	// check the modeenv
  2185  	m2, err := boot.ReadModeenv("")
  2186  	c.Assert(err, IsNil)
  2187  	// update assets are in the list
  2188  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, boot.BootAssetsMap{
  2189  		"asset": {dataHash},
  2190  	})
  2191  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, boot.BootAssetsMap{
  2192  		"asset": {dataHash},
  2193  		"shim":  {shimHash},
  2194  	})
  2195  	// unused files were dropped from cache
  2196  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2197  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2198  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2199  	})
  2200  	c.Check(resealCalls, Equals, 2)
  2201  }
  2202  
  2203  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsStableStateHappy(c *C) {
  2204  	// checked by resealKeyToModeenv
  2205  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2206  
  2207  	tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"})
  2208  
  2209  	data := []byte("foobar")
  2210  	// SHA3-384
  2211  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2212  	shim := []byte("shim")
  2213  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2214  
  2215  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil)
  2216  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil)
  2217  	// only asset for ubuntu-boot
  2218  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil)
  2219  	// shim and asset for ubuntu-seed
  2220  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil)
  2221  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2222  
  2223  	// mock the files in cache
  2224  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2225  		"shim-" + shimHash,
  2226  		"asset-" + dataHash,
  2227  	})
  2228  
  2229  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.kern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2230  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2231  
  2232  	tab.BootChainList = []bootloader.BootFile{
  2233  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2234  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2235  		runKernelBf,
  2236  	}
  2237  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2238  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2239  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2240  		recoveryKernelBf,
  2241  	}
  2242  
  2243  	uc20Model := boottest.MakeMockUC20Model()
  2244  
  2245  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2246  		return uc20Model, []*seed.Snap{mockNamedKernelSeedSnap(c, snap.R(1), "pc-kernel-recovery"), mockGadgetSeedSnap(c, nil)}, nil
  2247  	})
  2248  	defer restore()
  2249  
  2250  	// we were trying an update of boot assets
  2251  	m := &boot.Modeenv{
  2252  		Mode:           "run",
  2253  		Base:           s.base1.Filename(),
  2254  		CurrentKernels: []string{s.kern1.Filename()},
  2255  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2256  			"asset": {dataHash},
  2257  		},
  2258  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2259  			"asset": {dataHash},
  2260  			"shim":  {shimHash},
  2261  		},
  2262  		CurrentRecoverySystems:    []string{"system"},
  2263  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  2264  
  2265  		Model:          uc20Model.Model(),
  2266  		BrandID:        uc20Model.BrandID(),
  2267  		Grade:          string(uc20Model.Grade()),
  2268  		ModelSignKeyID: uc20Model.SignKeyID(),
  2269  	}
  2270  	r := setupUC20Bootenv(
  2271  		c,
  2272  		tab.MockBootloader,
  2273  		&bootenv20Setup{
  2274  			modeenv:    m,
  2275  			kern:       s.kern1,
  2276  			kernStatus: boot.DefaultStatus,
  2277  		},
  2278  	)
  2279  	defer r()
  2280  
  2281  	coreDev := boottest.MockUC20Device("", uc20Model)
  2282  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2283  
  2284  	resealCalls := 0
  2285  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2286  		resealCalls++
  2287  		return nil
  2288  	})
  2289  	defer restore()
  2290  
  2291  	// write boot-chains for current state that will stay unchanged
  2292  	bootChains := []boot.BootChain{{
  2293  		BrandID:        "my-brand",
  2294  		Model:          "my-model-uc20",
  2295  		Grade:          "dangerous",
  2296  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2297  		AssetChain: []boot.BootAsset{{
  2298  			Role: bootloader.RoleRecovery, Name: "shim",
  2299  			Hashes: []string{
  2300  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2301  			},
  2302  		}, {
  2303  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2304  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2305  			},
  2306  		}},
  2307  		Kernel:         "pc-kernel",
  2308  		KernelRevision: "1",
  2309  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  2310  	}, {
  2311  		BrandID:        "my-brand",
  2312  		Model:          "my-model-uc20",
  2313  		Grade:          "dangerous",
  2314  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2315  		AssetChain: []boot.BootAsset{{
  2316  			Role: bootloader.RoleRecovery, Name: "shim",
  2317  			Hashes: []string{
  2318  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2319  			},
  2320  		}, {
  2321  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2322  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2323  			},
  2324  		}},
  2325  		Kernel:         "pc-kernel-recovery",
  2326  		KernelRevision: "1",
  2327  		KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"},
  2328  	}}
  2329  
  2330  	recoveryBootChains := []boot.BootChain{bootChains[1]}
  2331  
  2332  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  2333  	c.Assert(err, IsNil)
  2334  
  2335  	err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0)
  2336  	c.Assert(err, IsNil)
  2337  
  2338  	// mark successful
  2339  	err = boot.MarkBootSuccessful(coreDev)
  2340  	c.Assert(err, IsNil)
  2341  
  2342  	// modeenv is unchanged
  2343  	m2, err := boot.ReadModeenv("")
  2344  	c.Assert(err, IsNil)
  2345  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2346  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2347  	// files are still in cache
  2348  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2349  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2350  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2351  	})
  2352  
  2353  	// boot chains were built
  2354  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  2355  		s.kern1.MountFile(),
  2356  	})
  2357  	// no actual reseal
  2358  	c.Check(resealCalls, Equals, 0)
  2359  }
  2360  
  2361  func (s *bootenv20Suite) TestMarkBootSuccessful20BootUnassertedKernelAssetsStableStateHappy(c *C) {
  2362  	// checked by resealKeyToModeenv
  2363  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  2364  
  2365  	tab := s.bootloaderWithTrustedAssets(c, []string{"nested/asset", "shim"})
  2366  
  2367  	data := []byte("foobar")
  2368  	// SHA3-384
  2369  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2370  	shim := []byte("shim")
  2371  	shimHash := "dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b"
  2372  
  2373  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "nested"), 0755), IsNil)
  2374  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested"), 0755), IsNil)
  2375  	// only asset for ubuntu-boot
  2376  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "nested/asset"), data, 0644), IsNil)
  2377  	// shim and asset for ubuntu-seed
  2378  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "nested/asset"), data, 0644), IsNil)
  2379  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "shim"), shim, 0644), IsNil)
  2380  
  2381  	// mock the files in cache
  2382  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2383  		"shim-" + shimHash,
  2384  		"asset-" + dataHash,
  2385  	})
  2386  
  2387  	runKernelBf := bootloader.NewBootFile(filepath.Join(s.ukern1.Filename()), "kernel.efi", bootloader.RoleRunMode)
  2388  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  2389  
  2390  	tab.BootChainList = []bootloader.BootFile{
  2391  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2392  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2393  		runKernelBf,
  2394  	}
  2395  	tab.RecoveryBootChainList = []bootloader.BootFile{
  2396  		bootloader.NewBootFile("", "shim", bootloader.RoleRecovery),
  2397  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  2398  		recoveryKernelBf,
  2399  	}
  2400  
  2401  	uc20Model := boottest.MakeMockUC20Model()
  2402  
  2403  	restore := boot.MockSeedReadSystemEssential(func(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*seed.Snap, error) {
  2404  		return uc20Model, []*seed.Snap{mockNamedKernelSeedSnap(c, snap.R(1), "pc-kernel-recovery"), mockGadgetSeedSnap(c, nil)}, nil
  2405  	})
  2406  	defer restore()
  2407  
  2408  	// we were trying an update of boot assets
  2409  	m := &boot.Modeenv{
  2410  		Mode:           "run",
  2411  		Base:           s.base1.Filename(),
  2412  		CurrentKernels: []string{s.ukern1.Filename()},
  2413  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2414  			"asset": {dataHash},
  2415  		},
  2416  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2417  			"asset": {dataHash},
  2418  			"shim":  {shimHash},
  2419  		},
  2420  		CurrentRecoverySystems:    []string{"system"},
  2421  		GoodRecoverySystems:       []string{"system"},
  2422  		CurrentKernelCommandLines: boot.BootCommandLines{"snapd_recovery_mode=run"},
  2423  		// leave this comment to keep old gofmt happy
  2424  		Model:          "my-model-uc20",
  2425  		BrandID:        "my-brand",
  2426  		Grade:          "dangerous",
  2427  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2428  	}
  2429  	r := setupUC20Bootenv(
  2430  		c,
  2431  		tab.MockBootloader,
  2432  		&bootenv20Setup{
  2433  			modeenv:    m,
  2434  			kern:       s.ukern1,
  2435  			kernStatus: boot.DefaultStatus,
  2436  		},
  2437  	)
  2438  	defer r()
  2439  
  2440  	coreDev := boottest.MockUC20Device("", uc20Model)
  2441  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2442  
  2443  	resealCalls := 0
  2444  	restore = boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  2445  		resealCalls++
  2446  		return nil
  2447  	})
  2448  	defer restore()
  2449  
  2450  	// write boot-chains for current state that will stay unchanged
  2451  	bootChains := []boot.BootChain{{
  2452  		BrandID:        "my-brand",
  2453  		Model:          "my-model-uc20",
  2454  		Grade:          "dangerous",
  2455  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2456  		AssetChain: []boot.BootAsset{{
  2457  			Role: bootloader.RoleRecovery, Name: "shim",
  2458  			Hashes: []string{
  2459  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2460  			},
  2461  		}, {
  2462  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2463  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2464  			},
  2465  		}},
  2466  		Kernel: "pc-kernel",
  2467  		// unasserted kernel snap
  2468  		KernelRevision: "",
  2469  		KernelCmdlines: []string{"snapd_recovery_mode=run"},
  2470  	}, {
  2471  		BrandID:        "my-brand",
  2472  		Model:          "my-model-uc20",
  2473  		Grade:          "dangerous",
  2474  		ModelSignKeyID: "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij",
  2475  		AssetChain: []boot.BootAsset{{
  2476  			Role: bootloader.RoleRecovery, Name: "shim",
  2477  			Hashes: []string{
  2478  				"dac0063e831d4b2e7a330426720512fc50fa315042f0bb30f9d1db73e4898dcb89119cac41fdfa62137c8931a50f9d7b",
  2479  			},
  2480  		}, {
  2481  			Role: bootloader.RoleRecovery, Name: "asset", Hashes: []string{
  2482  				"0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8",
  2483  			},
  2484  		}},
  2485  		Kernel:         "pc-kernel-recovery",
  2486  		KernelRevision: "1",
  2487  		KernelCmdlines: []string{"snapd_recovery_mode=recover snapd_recovery_system=system"},
  2488  	}}
  2489  
  2490  	recoveryBootChains := []boot.BootChain{bootChains[1]}
  2491  
  2492  	err := boot.WriteBootChains(boot.ToPredictableBootChains(bootChains), filepath.Join(dirs.SnapFDEDir, "boot-chains"), 0)
  2493  	c.Assert(err, IsNil)
  2494  
  2495  	err = boot.WriteBootChains(boot.ToPredictableBootChains(recoveryBootChains), filepath.Join(dirs.SnapFDEDir, "recovery-boot-chains"), 0)
  2496  	c.Assert(err, IsNil)
  2497  
  2498  	// mark successful
  2499  	err = boot.MarkBootSuccessful(coreDev)
  2500  	c.Assert(err, IsNil)
  2501  
  2502  	// modeenv is unchanged
  2503  	m2, err := boot.ReadModeenv("")
  2504  	c.Assert(err, IsNil)
  2505  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2506  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2507  	// files are still in cache
  2508  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2509  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-"+dataHash),
  2510  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "shim-"+shimHash),
  2511  	})
  2512  
  2513  	// boot chains were built
  2514  	c.Check(tab.BootChainKernelPath, DeepEquals, []string{
  2515  		s.ukern1.MountFile(),
  2516  	})
  2517  	// no actual reseal
  2518  	c.Check(resealCalls, Equals, 0)
  2519  }
  2520  
  2521  func (s *bootenv20Suite) TestMarkBootSuccessful20BootAssetsUpdateUnexpectedAsset(c *C) {
  2522  	tab := s.bootloaderWithTrustedAssets(c, []string{"EFI/asset"})
  2523  
  2524  	data := []byte("foobar")
  2525  	// SHA3-384
  2526  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  2527  
  2528  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI"), 0755), IsNil)
  2529  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI"), 0755), IsNil)
  2530  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "EFI/asset"), data, 0644), IsNil)
  2531  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/asset"), data, 0644), IsNil)
  2532  	// mock some state in the cache
  2533  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2534  		"asset-one",
  2535  		"asset-two",
  2536  	})
  2537  
  2538  	coreDev := boottest.MockUC20Device("", nil)
  2539  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2540  	model := coreDev.Model()
  2541  
  2542  	// we were trying an update of boot assets
  2543  	m := &boot.Modeenv{
  2544  		Mode:           "run",
  2545  		Base:           s.base1.Filename(),
  2546  		CurrentKernels: []string{s.kern1.Filename()},
  2547  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2548  			// hash will not match
  2549  			"asset": {"one", "two"},
  2550  		},
  2551  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2552  			"asset": {"one", "two"},
  2553  		},
  2554  		Model:          model.Model(),
  2555  		BrandID:        model.BrandID(),
  2556  		Grade:          string(model.Grade()),
  2557  		ModelSignKeyID: model.SignKeyID(),
  2558  	}
  2559  	r := setupUC20Bootenv(
  2560  		c,
  2561  		tab.MockBootloader,
  2562  		&bootenv20Setup{
  2563  			modeenv:    m,
  2564  			kern:       s.kern1,
  2565  			kernStatus: boot.DefaultStatus,
  2566  		},
  2567  	)
  2568  	defer r()
  2569  
  2570  	// mark successful
  2571  	err := boot.MarkBootSuccessful(coreDev)
  2572  	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))
  2573  
  2574  	// check the modeenv
  2575  	m2, err := boot.ReadModeenv("")
  2576  	c.Assert(err, IsNil)
  2577  	// modeenv is unchaged
  2578  	c.Check(m2.CurrentTrustedBootAssets, DeepEquals, m.CurrentTrustedBootAssets)
  2579  	c.Check(m2.CurrentTrustedRecoveryBootAssets, DeepEquals, m.CurrentTrustedRecoveryBootAssets)
  2580  	// nothing was removed from cache
  2581  	checkContentGlob(c, filepath.Join(dirs.SnapBootAssetsDir, "trusted", "*"), []string{
  2582  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-one"),
  2583  		filepath.Join(dirs.SnapBootAssetsDir, "trusted", "asset-two"),
  2584  	})
  2585  }
  2586  
  2587  func (s *bootenv20Suite) setupMarkBootSuccessful20CommandLine(c *C, model *asserts.Model, mode string, cmdlines boot.BootCommandLines) *boot.Modeenv {
  2588  	// mock some state in the cache
  2589  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  2590  		"asset-one",
  2591  	})
  2592  	// a pending kernel command line change
  2593  	m := &boot.Modeenv{
  2594  		Mode:           mode,
  2595  		Base:           s.base1.Filename(),
  2596  		CurrentKernels: []string{s.kern1.Filename()},
  2597  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  2598  			"asset": {"one"},
  2599  		},
  2600  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  2601  			"asset": {"one"},
  2602  		},
  2603  		CurrentKernelCommandLines: cmdlines,
  2604  
  2605  		Model:          model.Model(),
  2606  		BrandID:        model.BrandID(),
  2607  		Grade:          string(model.Grade()),
  2608  		ModelSignKeyID: model.SignKeyID(),
  2609  	}
  2610  	return m
  2611  }
  2612  
  2613  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedHappy(c *C) {
  2614  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2615  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2616  	coreDev := boottest.MockUC20Device("", nil)
  2617  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2618  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2619  		"snapd_recovery_mode=run panic=-1",
  2620  		"snapd_recovery_mode=run candidate panic=-1",
  2621  	})
  2622  
  2623  	r := setupUC20Bootenv(
  2624  		c,
  2625  		tab.MockBootloader,
  2626  		&bootenv20Setup{
  2627  			modeenv:    m,
  2628  			kern:       s.kern1,
  2629  			kernStatus: boot.DefaultStatus,
  2630  		},
  2631  	)
  2632  	defer r()
  2633  	// mark successful
  2634  	err := boot.MarkBootSuccessful(coreDev)
  2635  	c.Assert(err, IsNil)
  2636  
  2637  	// check the modeenv
  2638  	m2, err := boot.ReadModeenv("")
  2639  	c.Assert(err, IsNil)
  2640  	// modeenv is unchaged
  2641  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2642  		"snapd_recovery_mode=run candidate panic=-1",
  2643  	})
  2644  }
  2645  
  2646  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedOld(c *C) {
  2647  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1")
  2648  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2649  	coreDev := boottest.MockUC20Device("", nil)
  2650  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2651  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2652  		"snapd_recovery_mode=run panic=-1",
  2653  		"snapd_recovery_mode=run candidate panic=-1",
  2654  	})
  2655  	r := setupUC20Bootenv(
  2656  		c,
  2657  		tab.MockBootloader,
  2658  		&bootenv20Setup{
  2659  			modeenv:    m,
  2660  			kern:       s.kern1,
  2661  			kernStatus: boot.DefaultStatus,
  2662  		},
  2663  	)
  2664  	defer r()
  2665  
  2666  	// mark successful
  2667  	err := boot.MarkBootSuccessful(coreDev)
  2668  	c.Assert(err, IsNil)
  2669  
  2670  	// check the modeenv
  2671  	m2, err := boot.ReadModeenv("")
  2672  	c.Assert(err, IsNil)
  2673  	// modeenv is unchaged
  2674  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2675  		"snapd_recovery_mode=run panic=-1",
  2676  	})
  2677  }
  2678  
  2679  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedMismatch(c *C) {
  2680  	s.mockCmdline(c, "snapd_recovery_mode=run different")
  2681  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2682  	coreDev := boottest.MockUC20Device("", nil)
  2683  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2684  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2685  		"snapd_recovery_mode=run",
  2686  		"snapd_recovery_mode=run candidate",
  2687  	})
  2688  	r := setupUC20Bootenv(
  2689  		c,
  2690  		tab.MockBootloader,
  2691  		&bootenv20Setup{
  2692  			modeenv:    m,
  2693  			kern:       s.kern1,
  2694  			kernStatus: boot.DefaultStatus,
  2695  		},
  2696  	)
  2697  	defer r()
  2698  
  2699  	// mark successful
  2700  	err := boot.MarkBootSuccessful(coreDev)
  2701  	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`)
  2702  }
  2703  
  2704  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootSuccessful(c *C) {
  2705  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1")
  2706  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2707  	tab.StaticCommandLine = "panic=-1"
  2708  	coreDev := boottest.MockUC20Device("", nil)
  2709  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2710  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", nil)
  2711  	r := setupUC20Bootenv(
  2712  		c,
  2713  		tab.MockBootloader,
  2714  		&bootenv20Setup{
  2715  			modeenv:    m,
  2716  			kern:       s.kern1,
  2717  			kernStatus: boot.DefaultStatus,
  2718  		},
  2719  	)
  2720  	defer r()
  2721  
  2722  	// mark successful
  2723  	err := boot.MarkBootSuccessful(coreDev)
  2724  	c.Assert(err, IsNil)
  2725  
  2726  	// check the modeenv
  2727  	m2, err := boot.ReadModeenv("")
  2728  	c.Assert(err, IsNil)
  2729  	// modeenv is unchaged
  2730  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2731  		"snapd_recovery_mode=run panic=-1",
  2732  	})
  2733  }
  2734  
  2735  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedFallbackOnBootMismatch(c *C) {
  2736  	s.mockCmdline(c, "snapd_recovery_mode=run panic=-1 unexpected")
  2737  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2738  	tab.StaticCommandLine = "panic=-1"
  2739  	coreDev := boottest.MockUC20Device("", nil)
  2740  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2741  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", nil)
  2742  	r := setupUC20Bootenv(
  2743  		c,
  2744  		tab.MockBootloader,
  2745  		&bootenv20Setup{
  2746  			modeenv:    m,
  2747  			kern:       s.kern1,
  2748  			kernStatus: boot.DefaultStatus,
  2749  		},
  2750  	)
  2751  	defer r()
  2752  
  2753  	// mark successful
  2754  	err := boot.MarkBootSuccessful(coreDev)
  2755  	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"`)
  2756  }
  2757  
  2758  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineNonRunMode(c *C) {
  2759  	// recover mode
  2760  	s.mockCmdline(c, "snapd_recovery_mode=recover snapd_recovery_system=1234 panic=-1")
  2761  	tab := s.bootloaderWithTrustedAssets(c, []string{"asset"})
  2762  	tab.StaticCommandLine = "panic=-1"
  2763  	coreDev := boottest.MockUC20Device("", nil)
  2764  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2765  	// current command line does not match any of the run mode command lines
  2766  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "recover", boot.BootCommandLines{
  2767  		"snapd_recovery_mode=run panic=-1",
  2768  		"snapd_recovery_mode=run candidate panic=-1",
  2769  	})
  2770  	r := setupUC20Bootenv(
  2771  		c,
  2772  		tab.MockBootloader,
  2773  		&bootenv20Setup{
  2774  			modeenv:    m,
  2775  			kern:       s.kern1,
  2776  			kernStatus: boot.DefaultStatus,
  2777  		},
  2778  	)
  2779  	defer r()
  2780  
  2781  	// mark successful
  2782  	err := boot.MarkBootSuccessful(coreDev)
  2783  	c.Assert(err, IsNil)
  2784  
  2785  	// check the modeenv
  2786  	m2, err := boot.ReadModeenv("")
  2787  	c.Assert(err, IsNil)
  2788  	// modeenv is unchaged
  2789  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2790  		"snapd_recovery_mode=run panic=-1",
  2791  		"snapd_recovery_mode=run candidate panic=-1",
  2792  	})
  2793  }
  2794  
  2795  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineUpdatedNoFDEManagedBootloader(c *C) {
  2796  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2797  	tab := s.bootloaderWithTrustedAssets(c, nil)
  2798  	coreDev := boottest.MockUC20Device("", nil)
  2799  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2800  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", boot.BootCommandLines{
  2801  		"snapd_recovery_mode=run panic=-1",
  2802  		"snapd_recovery_mode=run candidate panic=-1",
  2803  	})
  2804  	// without encryption, the trusted assets are not tracked in the modeenv,
  2805  	// but we still may want to track command lines so that the gadget can
  2806  	// contribute to the system command line
  2807  	m.CurrentTrustedBootAssets = nil
  2808  	m.CurrentTrustedRecoveryBootAssets = nil
  2809  
  2810  	r := setupUC20Bootenv(
  2811  		c,
  2812  		tab.MockBootloader,
  2813  		&bootenv20Setup{
  2814  			modeenv:    m,
  2815  			kern:       s.kern1,
  2816  			kernStatus: boot.DefaultStatus,
  2817  		},
  2818  	)
  2819  	defer r()
  2820  
  2821  	// mark successful
  2822  	err := boot.MarkBootSuccessful(coreDev)
  2823  	c.Assert(err, IsNil)
  2824  
  2825  	// check the modeenv
  2826  	m2, err := boot.ReadModeenv("")
  2827  	c.Assert(err, IsNil)
  2828  	// modeenv is unchaged
  2829  	c.Check(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  2830  		"snapd_recovery_mode=run candidate panic=-1",
  2831  	})
  2832  }
  2833  
  2834  func (s *bootenv20Suite) TestMarkBootSuccessful20CommandLineCompatNonTrustedBootloader(c *C) {
  2835  	s.mockCmdline(c, "snapd_recovery_mode=run candidate panic=-1")
  2836  	// bootloader has no trusted assets
  2837  	bl := bootloadertest.Mock("not-trusted", "")
  2838  	bootloader.Force(bl)
  2839  	s.AddCleanup(func() { bootloader.Force(nil) })
  2840  	coreDev := boottest.MockUC20Device("", nil)
  2841  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2842  	m := s.setupMarkBootSuccessful20CommandLine(c, coreDev.Model(), "run", nil)
  2843  	// no trusted assets
  2844  	m.CurrentTrustedBootAssets = nil
  2845  	m.CurrentTrustedRecoveryBootAssets = nil
  2846  	// no kernel command lines tracked
  2847  	m.CurrentKernelCommandLines = nil
  2848  
  2849  	r := setupUC20Bootenv(
  2850  		c,
  2851  		bl,
  2852  		&bootenv20Setup{
  2853  			modeenv:    m,
  2854  			kern:       s.kern1,
  2855  			kernStatus: boot.DefaultStatus,
  2856  		},
  2857  	)
  2858  	defer r()
  2859  
  2860  	// mark successful
  2861  	err := boot.MarkBootSuccessful(coreDev)
  2862  	c.Assert(err, IsNil)
  2863  
  2864  	// check the modeenv
  2865  	m2, err := boot.ReadModeenv("")
  2866  	c.Assert(err, IsNil)
  2867  	// modeenv isn't changed
  2868  	c.Check(m2.CurrentKernelCommandLines, HasLen, 0)
  2869  }
  2870  
  2871  func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsCompat(c *C) {
  2872  	b := bootloadertest.Mock("mock", s.bootdir)
  2873  	s.forceBootloader(b)
  2874  
  2875  	m := &boot.Modeenv{
  2876  		Mode:                   "run",
  2877  		Base:                   s.base1.Filename(),
  2878  		CurrentKernels:         []string{s.kern1.Filename()},
  2879  		CurrentRecoverySystems: []string{"1234"},
  2880  	}
  2881  
  2882  	r := setupUC20Bootenv(
  2883  		c,
  2884  		b,
  2885  		&bootenv20Setup{
  2886  			modeenv:    m,
  2887  			kern:       s.kern1,
  2888  			kernStatus: boot.DefaultStatus,
  2889  		},
  2890  	)
  2891  	defer r()
  2892  
  2893  	coreDev := boottest.MockUC20Device("", nil)
  2894  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2895  	// mark successful
  2896  	err := boot.MarkBootSuccessful(coreDev)
  2897  	c.Assert(err, IsNil)
  2898  
  2899  	// check the modeenv
  2900  	m2, err := boot.ReadModeenv("")
  2901  	c.Assert(err, IsNil)
  2902  	// the list of good recovery systems has not been modified
  2903  	c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"})
  2904  	c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234"})
  2905  }
  2906  
  2907  func (s *bootenv20Suite) TestMarkBootSuccessful20SystemsPopulated(c *C) {
  2908  	b := bootloadertest.Mock("mock", s.bootdir)
  2909  	s.forceBootloader(b)
  2910  
  2911  	m := &boot.Modeenv{
  2912  		Mode:                   "run",
  2913  		Base:                   s.base1.Filename(),
  2914  		CurrentKernels:         []string{s.kern1.Filename()},
  2915  		CurrentRecoverySystems: []string{"1234", "9999"},
  2916  		GoodRecoverySystems:    []string{"1234"},
  2917  	}
  2918  
  2919  	r := setupUC20Bootenv(
  2920  		c,
  2921  		b,
  2922  		&bootenv20Setup{
  2923  			modeenv:    m,
  2924  			kern:       s.kern1,
  2925  			kernStatus: boot.DefaultStatus,
  2926  		},
  2927  	)
  2928  	defer r()
  2929  
  2930  	coreDev := boottest.MockUC20Device("", nil)
  2931  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2932  	// mark successful
  2933  	err := boot.MarkBootSuccessful(coreDev)
  2934  	c.Assert(err, IsNil)
  2935  
  2936  	// check the modeenv
  2937  	m2, err := boot.ReadModeenv("")
  2938  	c.Assert(err, IsNil)
  2939  	// good recovery systems has been populated
  2940  	c.Check(m2.GoodRecoverySystems, DeepEquals, []string{"1234"})
  2941  	c.Check(m2.CurrentRecoverySystems, DeepEquals, []string{"1234", "9999"})
  2942  }
  2943  
  2944  func (s *bootenv20Suite) TestMarkBootSuccessful20ModelSignKeyIDPopulated(c *C) {
  2945  	b := bootloadertest.Mock("mock", s.bootdir)
  2946  	s.forceBootloader(b)
  2947  
  2948  	coreDev := boottest.MockUC20Device("", nil)
  2949  	c.Assert(coreDev.HasModeenv(), Equals, true)
  2950  
  2951  	m := &boot.Modeenv{
  2952  		Mode:           "run",
  2953  		Base:           s.base1.Filename(),
  2954  		CurrentKernels: []string{s.kern1.Filename()},
  2955  		Model:          "my-model-uc20",
  2956  		BrandID:        "my-brand",
  2957  		Grade:          "dangerous",
  2958  		// sign key ID is unset
  2959  	}
  2960  
  2961  	r := setupUC20Bootenv(
  2962  		c,
  2963  		b,
  2964  		&bootenv20Setup{
  2965  			modeenv:    m,
  2966  			kern:       s.kern1,
  2967  			kernStatus: boot.DefaultStatus,
  2968  		},
  2969  	)
  2970  	defer r()
  2971  
  2972  	// mark successful
  2973  	err := boot.MarkBootSuccessful(coreDev)
  2974  	c.Assert(err, IsNil)
  2975  
  2976  	// check the modeenv
  2977  	m2, err := boot.ReadModeenv("")
  2978  	c.Assert(err, IsNil)
  2979  	// model's sign key ID has been set
  2980  	c.Check(m2.ModelSignKeyID, Equals, "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij")
  2981  	c.Check(m2.Model, Equals, "my-model-uc20")
  2982  	c.Check(m2.BrandID, Equals, "my-brand")
  2983  	c.Check(m2.Grade, Equals, "dangerous")
  2984  }
  2985  
  2986  type recoveryBootenv20Suite struct {
  2987  	baseBootenvSuite
  2988  
  2989  	bootloader *bootloadertest.MockBootloader
  2990  
  2991  	dev boot.Device
  2992  }
  2993  
  2994  var _ = Suite(&recoveryBootenv20Suite{})
  2995  
  2996  func (s *recoveryBootenv20Suite) SetUpTest(c *C) {
  2997  	s.baseBootenvSuite.SetUpTest(c)
  2998  
  2999  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
  3000  	s.forceBootloader(s.bootloader)
  3001  
  3002  	s.dev = boottest.MockUC20Device("", nil)
  3003  }
  3004  
  3005  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeHappy(c *C) {
  3006  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  3007  	c.Assert(err, IsNil)
  3008  	c.Check(s.bootloader.BootVars, DeepEquals, map[string]string{
  3009  		"snapd_recovery_system": "1234",
  3010  		"snapd_recovery_mode":   "install",
  3011  	})
  3012  }
  3013  
  3014  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeSetErr(c *C) {
  3015  	s.bootloader.SetErr = errors.New("no can do")
  3016  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  3017  	c.Assert(err, ErrorMatches, `no can do`)
  3018  }
  3019  
  3020  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeNonUC20(c *C) {
  3021  	non20Dev := boottest.MockDevice("some-snap")
  3022  	err := boot.SetRecoveryBootSystemAndMode(non20Dev, "1234", "install")
  3023  	c.Assert(err, Equals, boot.ErrUnsupportedSystemMode)
  3024  }
  3025  
  3026  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeErrClumsy(c *C) {
  3027  	err := boot.SetRecoveryBootSystemAndMode(s.dev, "", "install")
  3028  	c.Assert(err, ErrorMatches, "internal error: system label is unset")
  3029  	err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "")
  3030  	c.Assert(err, ErrorMatches, "internal error: system mode is unset")
  3031  }
  3032  
  3033  func (s *recoveryBootenv20Suite) TestSetRecoveryBootSystemAndModeRealHappy(c *C) {
  3034  	bootloader.Force(nil)
  3035  
  3036  	mockSeedGrubDir := filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI", "ubuntu")
  3037  	err := os.MkdirAll(mockSeedGrubDir, 0755)
  3038  	c.Assert(err, IsNil)
  3039  	err = ioutil.WriteFile(filepath.Join(mockSeedGrubDir, "grub.cfg"), nil, 0644)
  3040  	c.Assert(err, IsNil)
  3041  
  3042  	err = boot.SetRecoveryBootSystemAndMode(s.dev, "1234", "install")
  3043  	c.Assert(err, IsNil)
  3044  
  3045  	bl, err := bootloader.Find(boot.InitramfsUbuntuSeedDir, &bootloader.Options{Role: bootloader.RoleRecovery})
  3046  	c.Assert(err, IsNil)
  3047  
  3048  	blvars, err := bl.GetBootVars("snapd_recovery_mode", "snapd_recovery_system")
  3049  	c.Assert(err, IsNil)
  3050  	c.Check(blvars, DeepEquals, map[string]string{
  3051  		"snapd_recovery_system": "1234",
  3052  		"snapd_recovery_mode":   "install",
  3053  	})
  3054  }
  3055  
  3056  type bootConfigSuite struct {
  3057  	baseBootenvSuite
  3058  
  3059  	bootloader *bootloadertest.MockTrustedAssetsBootloader
  3060  	gadgetSnap string
  3061  }
  3062  
  3063  var _ = Suite(&bootConfigSuite{})
  3064  
  3065  func (s *bootConfigSuite) SetUpTest(c *C) {
  3066  	s.baseBootenvSuite.SetUpTest(c)
  3067  
  3068  	s.bootloader = bootloadertest.Mock("trusted", c.MkDir()).WithTrustedAssets()
  3069  	s.bootloader.StaticCommandLine = "this is mocked panic=-1"
  3070  	s.bootloader.CandidateStaticCommandLine = "mocked candidate panic=-1"
  3071  	s.forceBootloader(s.bootloader)
  3072  
  3073  	s.mockCmdline(c, "snapd_recovery_mode=run this is mocked panic=-1")
  3074  	s.gadgetSnap = snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3075  }
  3076  
  3077  func (s *bootConfigSuite) mockCmdline(c *C, cmdline string) {
  3078  	c.Assert(ioutil.WriteFile(s.cmdlineFile, []byte(cmdline), 0644), IsNil)
  3079  }
  3080  
  3081  func (s *bootConfigSuite) TestBootConfigUpdateHappyNoKeysNoReseal(c *C) {
  3082  	coreDev := boottest.MockUC20Device("", nil)
  3083  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3084  
  3085  	m := &boot.Modeenv{
  3086  		Mode: "run",
  3087  		CurrentKernelCommandLines: boot.BootCommandLines{
  3088  			"snapd_recovery_mode=run this is mocked panic=-1",
  3089  		},
  3090  	}
  3091  	c.Assert(m.WriteTo(""), IsNil)
  3092  
  3093  	resealCalls := 0
  3094  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3095  		resealCalls++
  3096  		return nil
  3097  	})
  3098  	defer restore()
  3099  
  3100  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3101  	c.Assert(err, IsNil)
  3102  	c.Check(updated, Equals, false)
  3103  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3104  	c.Check(resealCalls, Equals, 0)
  3105  }
  3106  
  3107  func (s *bootConfigSuite) TestBootConfigUpdateHappyWithReseal(c *C) {
  3108  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3109  
  3110  	coreDev := boottest.MockUC20Device("", nil)
  3111  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3112  
  3113  	runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  3114  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  3115  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  3116  		"asset-hash-1",
  3117  	})
  3118  
  3119  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3120  	s.bootloader.BootChainList = []bootloader.BootFile{
  3121  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  3122  		runKernelBf,
  3123  	}
  3124  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  3125  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  3126  		recoveryKernelBf,
  3127  	}
  3128  	m := &boot.Modeenv{
  3129  		Mode:           "run",
  3130  		CurrentKernels: []string{"pc-kernel_500.snap"},
  3131  		CurrentKernelCommandLines: boot.BootCommandLines{
  3132  			"snapd_recovery_mode=run this is mocked panic=-1",
  3133  		},
  3134  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  3135  			"asset": []string{"hash-1"},
  3136  		},
  3137  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  3138  			"asset": []string{"hash-1"},
  3139  		},
  3140  	}
  3141  	c.Assert(m.WriteTo(""), IsNil)
  3142  
  3143  	resealCalls := 0
  3144  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3145  		resealCalls++
  3146  		c.Assert(params, NotNil)
  3147  		c.Assert(params.ModelParams, HasLen, 1)
  3148  		c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{
  3149  			"snapd_recovery_mode=run mocked candidate panic=-1",
  3150  			"snapd_recovery_mode=run this is mocked panic=-1",
  3151  		})
  3152  		return nil
  3153  	})
  3154  	defer restore()
  3155  
  3156  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3157  	c.Assert(err, IsNil)
  3158  	c.Check(updated, Equals, false)
  3159  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3160  	c.Check(resealCalls, Equals, 1)
  3161  
  3162  	m2, err := boot.ReadModeenv("")
  3163  	c.Assert(err, IsNil)
  3164  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3165  		"snapd_recovery_mode=run this is mocked panic=-1",
  3166  		"snapd_recovery_mode=run mocked candidate panic=-1",
  3167  	})
  3168  }
  3169  
  3170  func (s *bootConfigSuite) TestBootConfigUpdateHappyNoChange(c *C) {
  3171  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3172  
  3173  	coreDev := boottest.MockUC20Device("", nil)
  3174  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3175  
  3176  	s.bootloader.StaticCommandLine = "mocked unchanged panic=-1"
  3177  	s.bootloader.CandidateStaticCommandLine = "mocked unchanged panic=-1"
  3178  	s.mockCmdline(c, "snapd_recovery_mode=run mocked unchanged panic=-1")
  3179  
  3180  	m := &boot.Modeenv{
  3181  		Mode: "run",
  3182  		CurrentKernelCommandLines: boot.BootCommandLines{
  3183  			"snapd_recovery_mode=run mocked unchanged panic=-1",
  3184  		},
  3185  	}
  3186  	c.Assert(m.WriteTo(""), IsNil)
  3187  
  3188  	resealCalls := 0
  3189  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3190  		resealCalls++
  3191  		return nil
  3192  	})
  3193  	defer restore()
  3194  
  3195  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3196  	c.Assert(err, IsNil)
  3197  	c.Check(updated, Equals, false)
  3198  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3199  	c.Check(resealCalls, Equals, 0)
  3200  
  3201  	m2, err := boot.ReadModeenv("")
  3202  	c.Assert(err, IsNil)
  3203  	c.Assert(m2.CurrentKernelCommandLines, HasLen, 1)
  3204  }
  3205  
  3206  func (s *bootConfigSuite) TestBootConfigUpdateNonUC20DoesNothing(c *C) {
  3207  	nonUC20coreDev := boottest.MockDevice("pc-kernel")
  3208  	c.Assert(nonUC20coreDev.HasModeenv(), Equals, false)
  3209  	updated, err := boot.UpdateManagedBootConfigs(nonUC20coreDev, s.gadgetSnap)
  3210  	c.Assert(err, IsNil)
  3211  	c.Check(updated, Equals, false)
  3212  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3213  }
  3214  
  3215  func (s *bootConfigSuite) TestBootConfigUpdateBadModeErr(c *C) {
  3216  	uc20Dev := boottest.MockUC20Device("recover", nil)
  3217  	c.Assert(uc20Dev.HasModeenv(), Equals, true)
  3218  	updated, err := boot.UpdateManagedBootConfigs(uc20Dev, s.gadgetSnap)
  3219  	c.Assert(err, ErrorMatches, "internal error: boot config can only be updated in run mode")
  3220  	c.Check(updated, Equals, false)
  3221  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3222  }
  3223  
  3224  func (s *bootConfigSuite) TestBootConfigUpdateFailErr(c *C) {
  3225  	coreDev := boottest.MockUC20Device("", nil)
  3226  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3227  
  3228  	m := &boot.Modeenv{
  3229  		Mode: "run",
  3230  		CurrentKernelCommandLines: boot.BootCommandLines{
  3231  			"snapd_recovery_mode=run this is mocked panic=-1",
  3232  		},
  3233  	}
  3234  	c.Assert(m.WriteTo(""), IsNil)
  3235  
  3236  	s.bootloader.UpdateErr = errors.New("update fail")
  3237  
  3238  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3239  	c.Assert(err, ErrorMatches, "update fail")
  3240  	c.Check(updated, Equals, false)
  3241  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3242  }
  3243  
  3244  func (s *bootConfigSuite) TestBootConfigUpdateCmdlineMismatchErr(c *C) {
  3245  	coreDev := boottest.MockUC20Device("", nil)
  3246  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3247  
  3248  	m := &boot.Modeenv{
  3249  		Mode: "run",
  3250  	}
  3251  	c.Assert(m.WriteTo(""), IsNil)
  3252  
  3253  	s.mockCmdline(c, "snapd_recovery_mode=run unexpected cmdline")
  3254  
  3255  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3256  	c.Assert(err, ErrorMatches, `internal error: current kernel command lines is unset`)
  3257  	c.Check(updated, Equals, false)
  3258  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3259  }
  3260  
  3261  func (s *bootConfigSuite) TestBootConfigUpdateNotManagedErr(c *C) {
  3262  	coreDev := boottest.MockUC20Device("", nil)
  3263  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3264  
  3265  	bl := bootloadertest.Mock("not-managed", c.MkDir())
  3266  	bootloader.Force(bl)
  3267  	defer bootloader.Force(nil)
  3268  
  3269  	m := &boot.Modeenv{
  3270  		Mode: "run",
  3271  	}
  3272  	c.Assert(m.WriteTo(""), IsNil)
  3273  
  3274  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3275  	c.Assert(err, IsNil)
  3276  	c.Check(updated, Equals, false)
  3277  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3278  }
  3279  
  3280  func (s *bootConfigSuite) TestBootConfigUpdateBootloaderFindErr(c *C) {
  3281  	coreDev := boottest.MockUC20Device("", nil)
  3282  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3283  
  3284  	bootloader.ForceError(errors.New("mocked find error"))
  3285  	defer bootloader.ForceError(nil)
  3286  
  3287  	m := &boot.Modeenv{
  3288  		Mode: "run",
  3289  	}
  3290  	c.Assert(m.WriteTo(""), IsNil)
  3291  
  3292  	updated, err := boot.UpdateManagedBootConfigs(coreDev, s.gadgetSnap)
  3293  	c.Assert(err, ErrorMatches, "internal error: cannot find trusted assets bootloader under .*: mocked find error")
  3294  	c.Check(updated, Equals, false)
  3295  	c.Check(s.bootloader.UpdateCalls, Equals, 0)
  3296  }
  3297  
  3298  func (s *bootConfigSuite) TestBootConfigUpdateWithGadgetAndReseal(c *C) {
  3299  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3300  
  3301  	gadgetSnap := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3302  		{"cmdline.extra", "foo bar baz"},
  3303  	})
  3304  	coreDev := boottest.MockUC20Device("", nil)
  3305  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3306  
  3307  	runKernelBf := bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  3308  	recoveryKernelBf := bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  3309  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  3310  		"asset-hash-1",
  3311  	})
  3312  
  3313  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3314  	s.bootloader.BootChainList = []bootloader.BootFile{
  3315  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  3316  		runKernelBf,
  3317  	}
  3318  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  3319  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  3320  		recoveryKernelBf,
  3321  	}
  3322  	m := &boot.Modeenv{
  3323  		Mode:           "run",
  3324  		CurrentKernels: []string{"pc-kernel_500.snap"},
  3325  		CurrentKernelCommandLines: boot.BootCommandLines{
  3326  			// the extra arguments would be included in the current
  3327  			// command line already
  3328  			"snapd_recovery_mode=run this is mocked panic=-1 foo bar baz",
  3329  		},
  3330  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  3331  			"asset": []string{"hash-1"},
  3332  		},
  3333  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  3334  			"asset": []string{"hash-1"},
  3335  		},
  3336  	}
  3337  	c.Assert(m.WriteTo(""), IsNil)
  3338  
  3339  	resealCalls := 0
  3340  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3341  		resealCalls++
  3342  		c.Assert(params, NotNil)
  3343  		c.Assert(params.ModelParams, HasLen, 1)
  3344  		c.Check(params.ModelParams[0].KernelCmdlines, DeepEquals, []string{
  3345  			"snapd_recovery_mode=run mocked candidate panic=-1 foo bar baz",
  3346  			"snapd_recovery_mode=run this is mocked panic=-1 foo bar baz",
  3347  		})
  3348  		return nil
  3349  	})
  3350  	defer restore()
  3351  
  3352  	updated, err := boot.UpdateManagedBootConfigs(coreDev, gadgetSnap)
  3353  	c.Assert(err, IsNil)
  3354  	c.Check(updated, Equals, false)
  3355  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3356  	c.Check(resealCalls, Equals, 1)
  3357  
  3358  	m2, err := boot.ReadModeenv("")
  3359  	c.Assert(err, IsNil)
  3360  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3361  		"snapd_recovery_mode=run this is mocked panic=-1 foo bar baz",
  3362  		"snapd_recovery_mode=run mocked candidate panic=-1 foo bar baz",
  3363  	})
  3364  }
  3365  
  3366  func (s *bootConfigSuite) TestBootConfigUpdateWithGadgetFullAndReseal(c *C) {
  3367  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3368  
  3369  	gadgetSnap := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3370  		{"cmdline.full", "foo bar baz"},
  3371  	})
  3372  	coreDev := boottest.MockUC20Device("", nil)
  3373  	c.Assert(coreDev.HasModeenv(), Equals, true)
  3374  
  3375  	// a minimal bootloader and modeenv setup that works because reseal is
  3376  	// not executed
  3377  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3378  	m := &boot.Modeenv{
  3379  		Mode: "run",
  3380  		CurrentKernelCommandLines: boot.BootCommandLines{
  3381  			// the full arguments would be included in the current
  3382  			// command line already
  3383  			"snapd_recovery_mode=run foo bar baz",
  3384  		},
  3385  	}
  3386  	c.Assert(m.WriteTo(""), IsNil)
  3387  
  3388  	s.bootloader.Updated = true
  3389  
  3390  	resealCalls := 0
  3391  	// reseal does not happen, because the gadget overrides the static
  3392  	// command line which is part of boot config, thus there's no resulting
  3393  	// change in the command lines tracked in modeenv and no need to reseal
  3394  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3395  		resealCalls++
  3396  		return fmt.Errorf("unexpected call")
  3397  	})
  3398  	defer restore()
  3399  
  3400  	updated, err := boot.UpdateManagedBootConfigs(coreDev, gadgetSnap)
  3401  	c.Assert(err, IsNil)
  3402  	c.Check(updated, Equals, true)
  3403  	c.Check(s.bootloader.UpdateCalls, Equals, 1)
  3404  	c.Check(resealCalls, Equals, 0)
  3405  
  3406  	m2, err := boot.ReadModeenv("")
  3407  	c.Assert(err, IsNil)
  3408  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3409  		"snapd_recovery_mode=run foo bar baz",
  3410  	})
  3411  }
  3412  
  3413  type bootKernelCommandLineSuite struct {
  3414  	baseBootenvSuite
  3415  
  3416  	bootloader            *bootloadertest.MockTrustedAssetsBootloader
  3417  	gadgetSnap            string
  3418  	uc20dev               boot.Device
  3419  	recoveryKernelBf      bootloader.BootFile
  3420  	runKernelBf           bootloader.BootFile
  3421  	modeenvWithEncryption *boot.Modeenv
  3422  	resealCalls           int
  3423  	resealCommandLines    [][]string
  3424  }
  3425  
  3426  var _ = Suite(&bootKernelCommandLineSuite{})
  3427  
  3428  func (s *bootKernelCommandLineSuite) SetUpTest(c *C) {
  3429  	s.baseBootenvSuite.SetUpTest(c)
  3430  
  3431  	data := []byte("foobar")
  3432  	// SHA3-384
  3433  	dataHash := "0fa8abfbdaf924ad307b74dd2ed183b9a4a398891a2f6bac8fd2db7041b77f068580f9c6c66f699b496c2da1cbcc7ed8"
  3434  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir), 0755), IsNil)
  3435  	c.Assert(os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir), 0755), IsNil)
  3436  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuBootDir, "asset"), data, 0644), IsNil)
  3437  	c.Assert(ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "asset"), data, 0644), IsNil)
  3438  
  3439  	s.bootloader = bootloadertest.Mock("trusted", c.MkDir()).WithTrustedAssets()
  3440  	s.bootloader.TrustedAssetsList = []string{"asset"}
  3441  	s.bootloader.StaticCommandLine = "static mocked panic=-1"
  3442  	s.bootloader.CandidateStaticCommandLine = "mocked candidate panic=-1"
  3443  	s.forceBootloader(s.bootloader)
  3444  
  3445  	s.mockCmdline(c, "snapd_recovery_mode=run this is mocked panic=-1")
  3446  	s.gadgetSnap = snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3447  	s.uc20dev = boottest.MockUC20Device("", boottest.MakeMockUC20Model(nil))
  3448  	s.runKernelBf = bootloader.NewBootFile("/var/lib/snapd/snap/pc-kernel_600.snap", "kernel.efi", bootloader.RoleRunMode)
  3449  	s.recoveryKernelBf = bootloader.NewBootFile("/var/lib/snapd/seed/snaps/pc-kernel_1.snap", "kernel.efi", bootloader.RoleRecovery)
  3450  	mockAssetsCache(c, dirs.GlobalRootDir, "trusted", []string{
  3451  		"asset-" + dataHash,
  3452  	})
  3453  
  3454  	s.bootloader.BootChainList = []bootloader.BootFile{
  3455  		bootloader.NewBootFile("", "asset", bootloader.RoleRunMode),
  3456  		s.runKernelBf,
  3457  	}
  3458  	s.bootloader.RecoveryBootChainList = []bootloader.BootFile{
  3459  		bootloader.NewBootFile("", "asset", bootloader.RoleRecovery),
  3460  		s.recoveryKernelBf,
  3461  	}
  3462  	s.modeenvWithEncryption = &boot.Modeenv{
  3463  		Mode:           "run",
  3464  		CurrentKernels: []string{"pc-kernel_500.snap"},
  3465  		Base:           "core20_1.snap",
  3466  		BaseStatus:     boot.DefaultStatus,
  3467  		CurrentKernelCommandLines: boot.BootCommandLines{
  3468  			// the extra arguments would be included in the current
  3469  			// command line already
  3470  			"snapd_recovery_mode=run static mocked panic=-1",
  3471  		},
  3472  		CurrentTrustedRecoveryBootAssets: boot.BootAssetsMap{
  3473  			"asset": []string{dataHash},
  3474  		},
  3475  		CurrentTrustedBootAssets: boot.BootAssetsMap{
  3476  			"asset": []string{dataHash},
  3477  		},
  3478  	}
  3479  	s.bootloader.SetBootVars(map[string]string{
  3480  		"snap_kernel": "pc-kernel_500.snap",
  3481  	})
  3482  	s.bootloader.SetBootVarsCalls = 0
  3483  
  3484  	s.resealCommandLines = nil
  3485  	s.resealCalls = 0
  3486  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3487  		s.resealCalls++
  3488  		c.Assert(params, NotNil)
  3489  		c.Assert(params.ModelParams, HasLen, 1)
  3490  		s.resealCommandLines = append(s.resealCommandLines, params.ModelParams[0].KernelCmdlines)
  3491  		return nil
  3492  	})
  3493  	s.AddCleanup(restore)
  3494  }
  3495  
  3496  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateNonUC20(c *C) {
  3497  	nonUC20dev := boottest.MockDevice("")
  3498  
  3499  	// gadget which would otherwise trigger an update
  3500  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3501  		{"cmdline.extra", "foo"},
  3502  	})
  3503  
  3504  	reboot, err := boot.UpdateCommandLineForGadgetComponent(nonUC20dev, sf)
  3505  	c.Assert(err, ErrorMatches, "internal error: command line component cannot be updated on non UC20 devices")
  3506  	c.Assert(reboot, Equals, false)
  3507  }
  3508  
  3509  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20NotManagedBootloader(c *C) {
  3510  	// gadget which would otherwise trigger an update
  3511  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3512  		{"cmdline.extra", "foo"},
  3513  	})
  3514  
  3515  	// but the bootloader is not managed by snapd
  3516  	bl := bootloadertest.Mock("not-managed", c.MkDir())
  3517  	bl.SetErr = fmt.Errorf("unexpected call")
  3518  	s.forceBootloader(bl)
  3519  
  3520  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3521  	c.Assert(err, IsNil)
  3522  	c.Assert(reboot, Equals, false)
  3523  	c.Check(bl.SetBootVarsCalls, Equals, 0)
  3524  }
  3525  
  3526  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20ArgsAdded(c *C) {
  3527  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3528  
  3529  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3530  		{"cmdline.extra", "args from gadget"},
  3531  	})
  3532  
  3533  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3534  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3535  
  3536  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3537  	c.Assert(err, IsNil)
  3538  	c.Assert(reboot, Equals, true)
  3539  
  3540  	// reseal happened
  3541  	c.Check(s.resealCalls, Equals, 1)
  3542  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3543  		"snapd_recovery_mode=run static mocked panic=-1",
  3544  		"snapd_recovery_mode=run static mocked panic=-1 args from gadget",
  3545  	}})
  3546  
  3547  	// modeenv has been updated
  3548  	newM, err := boot.ReadModeenv("")
  3549  	c.Assert(err, IsNil)
  3550  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3551  		"snapd_recovery_mode=run static mocked panic=-1",
  3552  		"snapd_recovery_mode=run static mocked panic=-1 args from gadget",
  3553  	})
  3554  
  3555  	// bootloader variables too
  3556  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3557  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3558  	c.Assert(err, IsNil)
  3559  	c.Check(args, DeepEquals, map[string]string{
  3560  		"snapd_extra_cmdline_args": "args from gadget",
  3561  		"snapd_full_cmdline_args":  "",
  3562  	})
  3563  }
  3564  
  3565  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20ArgsSwitch(c *C) {
  3566  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3567  
  3568  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3569  		{"cmdline.extra", "no change"},
  3570  	})
  3571  
  3572  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1 no change"}
  3573  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3574  	err := s.bootloader.SetBootVars(map[string]string{
  3575  		"snapd_extra_cmdline_args": "no change",
  3576  		// this is intentionally filled and will be cleared
  3577  		"snapd_full_cmdline_args": "canary",
  3578  	})
  3579  	c.Assert(err, IsNil)
  3580  	s.bootloader.SetBootVarsCalls = 0
  3581  
  3582  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3583  	c.Assert(err, IsNil)
  3584  	c.Assert(reboot, Equals, false)
  3585  
  3586  	// no reseal needed
  3587  	c.Check(s.resealCalls, Equals, 0)
  3588  
  3589  	newM, err := boot.ReadModeenv("")
  3590  	c.Assert(err, IsNil)
  3591  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3592  		"snapd_recovery_mode=run static mocked panic=-1 no change",
  3593  	})
  3594  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3595  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3596  	c.Assert(err, IsNil)
  3597  	c.Check(args, DeepEquals, map[string]string{
  3598  		"snapd_extra_cmdline_args": "no change",
  3599  		// canary is still present, as nothing was modified
  3600  		"snapd_full_cmdline_args": "canary",
  3601  	})
  3602  
  3603  	// let's change them now
  3604  	sfChanged := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3605  		{"cmdline.extra", "changed"},
  3606  	})
  3607  
  3608  	reboot, err = boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sfChanged)
  3609  	c.Assert(err, IsNil)
  3610  	c.Assert(reboot, Equals, true)
  3611  
  3612  	// reseal was applied
  3613  	c.Check(s.resealCalls, Equals, 1)
  3614  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3615  		// those come from boot chains which use predictable sorting
  3616  		"snapd_recovery_mode=run static mocked panic=-1 changed",
  3617  		"snapd_recovery_mode=run static mocked panic=-1 no change",
  3618  	}})
  3619  
  3620  	// modeenv has been updated
  3621  	newM, err = boot.ReadModeenv("")
  3622  	c.Assert(err, IsNil)
  3623  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3624  		"snapd_recovery_mode=run static mocked panic=-1 no change",
  3625  		// new ones are appended
  3626  		"snapd_recovery_mode=run static mocked panic=-1 changed",
  3627  	})
  3628  	// and bootloader env too
  3629  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3630  	c.Assert(err, IsNil)
  3631  	c.Check(args, DeepEquals, map[string]string{
  3632  		"snapd_extra_cmdline_args": "changed",
  3633  		// canary has been cleared as bootenv was modified
  3634  		"snapd_full_cmdline_args": "",
  3635  	})
  3636  }
  3637  
  3638  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20UnencryptedArgsRemoved(c *C) {
  3639  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3640  
  3641  	// pretend we used to have additional arguments from the gadget, but
  3642  	// those will be gone with new update
  3643  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3644  
  3645  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1 from-gadget"}
  3646  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3647  	err := s.bootloader.SetBootVars(map[string]string{
  3648  		"snapd_extra_cmdline_args": "from-gadget",
  3649  		// this is intentionally filled and will be cleared
  3650  		"snapd_full_cmdline_args": "canary",
  3651  	})
  3652  	c.Assert(err, IsNil)
  3653  	s.bootloader.SetBootVarsCalls = 0
  3654  
  3655  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3656  	c.Assert(err, IsNil)
  3657  	c.Assert(reboot, Equals, true)
  3658  
  3659  	c.Check(s.resealCalls, Equals, 1)
  3660  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3661  		"snapd_recovery_mode=run static mocked panic=-1",
  3662  		"snapd_recovery_mode=run static mocked panic=-1 from-gadget",
  3663  	}})
  3664  
  3665  	newM, err := boot.ReadModeenv("")
  3666  	c.Assert(err, IsNil)
  3667  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3668  		"snapd_recovery_mode=run static mocked panic=-1 from-gadget",
  3669  		"snapd_recovery_mode=run static mocked panic=-1",
  3670  	})
  3671  	// bootloader variables were explicitly cleared
  3672  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3673  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3674  	c.Assert(err, IsNil)
  3675  	c.Check(args, DeepEquals, map[string]string{
  3676  		"snapd_extra_cmdline_args": "",
  3677  		"snapd_full_cmdline_args":  "",
  3678  	})
  3679  }
  3680  
  3681  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20SetError(c *C) {
  3682  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3683  
  3684  	// pretend we used to have additional arguments from the gadget, but
  3685  	// those will be gone with new update
  3686  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3687  		{"cmdline.extra", "this-is-not-applied"},
  3688  	})
  3689  
  3690  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3691  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3692  
  3693  	s.bootloader.SetErr = fmt.Errorf("set fails")
  3694  
  3695  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3696  	c.Assert(err, ErrorMatches, "cannot set run system kernel command line arguments: set fails")
  3697  	c.Assert(reboot, Equals, false)
  3698  	// set boot vars was called and failed
  3699  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3700  
  3701  	// reseal with new parameters happened though
  3702  	c.Check(s.resealCalls, Equals, 1)
  3703  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3704  		"snapd_recovery_mode=run static mocked panic=-1",
  3705  		"snapd_recovery_mode=run static mocked panic=-1 this-is-not-applied",
  3706  	}})
  3707  
  3708  	newM, err := boot.ReadModeenv("")
  3709  	c.Assert(err, IsNil)
  3710  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3711  		"snapd_recovery_mode=run static mocked panic=-1",
  3712  		// this will be cleared on next reboot or will get overwritten
  3713  		// by an update
  3714  		"snapd_recovery_mode=run static mocked panic=-1 this-is-not-applied",
  3715  	})
  3716  }
  3717  
  3718  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateWithResealError(c *C) {
  3719  	gadgetSnap := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3720  		{"cmdline.extra", "args from gadget"},
  3721  	})
  3722  
  3723  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3724  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3725  
  3726  	resealCalls := 0
  3727  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3728  		resealCalls++
  3729  		return fmt.Errorf("reseal fails")
  3730  	})
  3731  	defer restore()
  3732  
  3733  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, gadgetSnap)
  3734  	c.Assert(err, ErrorMatches, "cannot reseal the encryption key: reseal fails")
  3735  	c.Check(reboot, Equals, false)
  3736  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3737  	c.Check(resealCalls, Equals, 1)
  3738  
  3739  	m2, err := boot.ReadModeenv("")
  3740  	c.Assert(err, IsNil)
  3741  	c.Assert(m2.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3742  		"snapd_recovery_mode=run static mocked panic=-1",
  3743  		"snapd_recovery_mode=run static mocked panic=-1 args from gadget",
  3744  	})
  3745  }
  3746  
  3747  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20TransitionFullExtraAndBack(c *C) {
  3748  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3749  
  3750  	// no command line arguments from gadget
  3751  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3752  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3753  	err := s.bootloader.SetBootVars(map[string]string{
  3754  		// those are intentionally filled by the test
  3755  		"snapd_extra_cmdline_args": "canary",
  3756  		"snapd_full_cmdline_args":  "canary",
  3757  	})
  3758  	c.Assert(err, IsNil)
  3759  	s.bootloader.SetBootVarsCalls = 0
  3760  
  3761  	// transition to gadget with cmdline.extra
  3762  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3763  		{"cmdline.extra", "extra args"},
  3764  	})
  3765  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3766  	c.Assert(err, IsNil)
  3767  	c.Assert(reboot, Equals, true)
  3768  	c.Check(s.resealCalls, Equals, 1)
  3769  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3770  		// those come from boot chains which use predictable sorting
  3771  		"snapd_recovery_mode=run static mocked panic=-1",
  3772  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3773  	}})
  3774  	s.resealCommandLines = nil
  3775  
  3776  	newM, err := boot.ReadModeenv("")
  3777  	c.Assert(err, IsNil)
  3778  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3779  		"snapd_recovery_mode=run static mocked panic=-1",
  3780  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3781  	})
  3782  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3783  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3784  	c.Assert(err, IsNil)
  3785  	c.Check(args, DeepEquals, map[string]string{
  3786  		"snapd_extra_cmdline_args": "extra args",
  3787  		// canary has been cleared
  3788  		"snapd_full_cmdline_args": "",
  3789  	})
  3790  	// this normally happens after booting
  3791  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1 extra args"}
  3792  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3793  
  3794  	// transition to full override from gadget
  3795  	sfFull := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3796  		{"cmdline.full", "full args"},
  3797  	})
  3798  	reboot, err = boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sfFull)
  3799  	c.Assert(err, IsNil)
  3800  	c.Assert(reboot, Equals, true)
  3801  	c.Check(s.resealCalls, Equals, 2)
  3802  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3803  		// those come from boot chains which use predictable sorting
  3804  		"snapd_recovery_mode=run full args",
  3805  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3806  	}})
  3807  	s.resealCommandLines = nil
  3808  	// modeenv has been updated
  3809  	newM, err = boot.ReadModeenv("")
  3810  	c.Assert(err, IsNil)
  3811  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3812  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3813  		// new ones are appended
  3814  		"snapd_recovery_mode=run full args",
  3815  	})
  3816  	// and bootloader env too
  3817  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3818  	c.Assert(err, IsNil)
  3819  	c.Check(args, DeepEquals, map[string]string{
  3820  		// cleared
  3821  		"snapd_extra_cmdline_args": "",
  3822  		// and full arguments were set
  3823  		"snapd_full_cmdline_args": "full args",
  3824  	})
  3825  	// this normally happens after booting
  3826  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run full args"}
  3827  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3828  
  3829  	// transition back to no arguments from the gadget
  3830  	sfNone := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, nil)
  3831  	reboot, err = boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sfNone)
  3832  	c.Assert(err, IsNil)
  3833  	c.Assert(reboot, Equals, true)
  3834  	c.Check(s.resealCalls, Equals, 3)
  3835  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3836  		// those come from boot chains which use predictable sorting
  3837  		"snapd_recovery_mode=run full args",
  3838  		"snapd_recovery_mode=run static mocked panic=-1",
  3839  	}})
  3840  	// modeenv has been updated again
  3841  	newM, err = boot.ReadModeenv("")
  3842  	c.Assert(err, IsNil)
  3843  	c.Check(newM.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3844  		"snapd_recovery_mode=run full args",
  3845  		// new ones are appended
  3846  		"snapd_recovery_mode=run static mocked panic=-1",
  3847  	})
  3848  	// and bootloader env too
  3849  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3850  	c.Assert(err, IsNil)
  3851  	c.Check(args, DeepEquals, map[string]string{
  3852  		// both env variables have been cleared
  3853  		"snapd_extra_cmdline_args": "",
  3854  		"snapd_full_cmdline_args":  "",
  3855  	})
  3856  }
  3857  
  3858  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20OverSpuriousRebootsBeforeBootVarsSet(c *C) {
  3859  	// simulate spurious reboots
  3860  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3861  
  3862  	resealPanic := false
  3863  	restore := boot.MockSecbootResealKeys(func(params *secboot.ResealKeysParams) error {
  3864  		s.resealCalls++
  3865  		c.Assert(params, NotNil)
  3866  		c.Assert(params.ModelParams, HasLen, 1)
  3867  		s.resealCommandLines = append(s.resealCommandLines, params.ModelParams[0].KernelCmdlines)
  3868  		if resealPanic {
  3869  			panic("reseal panic")
  3870  		}
  3871  		return nil
  3872  	})
  3873  	defer restore()
  3874  
  3875  	// no command line arguments from gadget
  3876  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  3877  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  3878  
  3879  	cmdlineFile := filepath.Join(c.MkDir(), "cmdline")
  3880  	err := ioutil.WriteFile(cmdlineFile, []byte("snapd_recovery_mode=run static mocked panic=-1"), 0644)
  3881  	c.Assert(err, IsNil)
  3882  	restore = osutil.MockProcCmdline(cmdlineFile)
  3883  	s.AddCleanup(restore)
  3884  
  3885  	err = s.bootloader.SetBootVars(map[string]string{
  3886  		// those are intentionally filled by the test
  3887  		"snapd_extra_cmdline_args": "canary",
  3888  		"snapd_full_cmdline_args":  "canary",
  3889  	})
  3890  	c.Assert(err, IsNil)
  3891  	s.bootloader.SetBootVarsCalls = 0
  3892  
  3893  	restoreBootloaderNoPanic := s.bootloader.SetMockToPanic("SetBootVars")
  3894  	defer restoreBootloaderNoPanic()
  3895  
  3896  	// transition to gadget with cmdline.extra
  3897  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  3898  		{"cmdline.extra", "extra args"},
  3899  	})
  3900  
  3901  	// let's panic on reseal first
  3902  	resealPanic = true
  3903  	c.Assert(func() {
  3904  		boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3905  	}, PanicMatches, "reseal panic")
  3906  	c.Check(s.resealCalls, Equals, 1)
  3907  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3908  		// those come from boot chains which use predictable sorting
  3909  		"snapd_recovery_mode=run static mocked panic=-1",
  3910  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3911  	}})
  3912  	// bootenv hasn't been updated yet
  3913  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3914  	// but modeenv has already been updated
  3915  	m, err := boot.ReadModeenv("")
  3916  	c.Assert(err, IsNil)
  3917  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3918  		"snapd_recovery_mode=run static mocked panic=-1",
  3919  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3920  	})
  3921  
  3922  	// REBOOT
  3923  	resealPanic = false
  3924  	err = boot.MarkBootSuccessful(s.uc20dev)
  3925  	c.Assert(err, IsNil)
  3926  	// we resealed after reboot, since modeenv was updated and carries the
  3927  	// current command line only
  3928  	c.Check(s.resealCalls, Equals, 2)
  3929  	m, err = boot.ReadModeenv("")
  3930  	c.Assert(err, IsNil)
  3931  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3932  		"snapd_recovery_mode=run static mocked panic=-1",
  3933  	})
  3934  
  3935  	// try the update again, but no panic in reseal this time
  3936  	s.resealCalls = 0
  3937  	s.resealCommandLines = nil
  3938  	resealPanic = false
  3939  	// but panic in set
  3940  	c.Assert(func() {
  3941  		boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3942  	}, PanicMatches, "mocked reboot panic in SetBootVars")
  3943  	c.Check(s.resealCalls, Equals, 1)
  3944  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  3945  		// those come from boot chains which use predictable sorting
  3946  		"snapd_recovery_mode=run static mocked panic=-1",
  3947  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3948  	}})
  3949  	// the call to bootloader wasn't counted, because it called panic
  3950  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  3951  	m, err = boot.ReadModeenv("")
  3952  	c.Assert(err, IsNil)
  3953  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3954  		"snapd_recovery_mode=run static mocked panic=-1",
  3955  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3956  	})
  3957  
  3958  	// REBOOT
  3959  	err = boot.MarkBootSuccessful(s.uc20dev)
  3960  	c.Assert(err, IsNil)
  3961  	// we resealed after reboot again
  3962  	c.Check(s.resealCalls, Equals, 2)
  3963  	m, err = boot.ReadModeenv("")
  3964  	c.Assert(err, IsNil)
  3965  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3966  		"snapd_recovery_mode=run static mocked panic=-1",
  3967  	})
  3968  
  3969  	// try again, for the last time, things should go smoothly
  3970  	s.resealCalls = 0
  3971  	s.resealCommandLines = nil
  3972  	restoreBootloaderNoPanic()
  3973  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  3974  	c.Assert(err, IsNil)
  3975  	c.Check(reboot, Equals, true)
  3976  	c.Check(s.resealCalls, Equals, 1)
  3977  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  3978  	// all done, modeenv
  3979  	m, err = boot.ReadModeenv("")
  3980  	c.Assert(err, IsNil)
  3981  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  3982  		"snapd_recovery_mode=run static mocked panic=-1",
  3983  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  3984  	})
  3985  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  3986  	c.Assert(err, IsNil)
  3987  	c.Check(args, DeepEquals, map[string]string{
  3988  		"snapd_extra_cmdline_args": "extra args",
  3989  		// canary has been cleared
  3990  		"snapd_full_cmdline_args": "",
  3991  	})
  3992  }
  3993  
  3994  func (s *bootKernelCommandLineSuite) TestCommandLineUpdateUC20OverSpuriousRebootsAfterBootVars(c *C) {
  3995  	// simulate spurious reboots
  3996  	s.stampSealedKeys(c, dirs.GlobalRootDir)
  3997  
  3998  	// no command line arguments from gadget
  3999  	s.modeenvWithEncryption.CurrentKernelCommandLines = []string{"snapd_recovery_mode=run static mocked panic=-1"}
  4000  	c.Assert(s.modeenvWithEncryption.WriteTo(""), IsNil)
  4001  
  4002  	cmdlineFile := filepath.Join(c.MkDir(), "cmdline")
  4003  	restore := osutil.MockProcCmdline(cmdlineFile)
  4004  	s.AddCleanup(restore)
  4005  
  4006  	err := s.bootloader.SetBootVars(map[string]string{
  4007  		// those are intentionally filled by the test
  4008  		"snapd_extra_cmdline_args": "canary",
  4009  		"snapd_full_cmdline_args":  "canary",
  4010  	})
  4011  	c.Assert(err, IsNil)
  4012  	s.bootloader.SetBootVarsCalls = 0
  4013  
  4014  	// transition to gadget with cmdline.extra
  4015  	sf := snaptest.MakeTestSnapWithFiles(c, gadgetSnapYaml, [][]string{
  4016  		{"cmdline.extra", "extra args"},
  4017  	})
  4018  
  4019  	// let's panic after setting bootenv, but before returning, such that if
  4020  	// executed by a task handler, the task's status would not get updated
  4021  	s.bootloader.SetErrFunc = func() error {
  4022  		panic("mocked reboot panic after SetBootVars")
  4023  	}
  4024  	c.Assert(func() {
  4025  		boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  4026  	}, PanicMatches, "mocked reboot panic after SetBootVars")
  4027  	c.Check(s.resealCalls, Equals, 1)
  4028  	c.Check(s.resealCommandLines, DeepEquals, [][]string{{
  4029  		// those come from boot chains which use predictable sorting
  4030  		"snapd_recovery_mode=run static mocked panic=-1",
  4031  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4032  	}})
  4033  	// the call to bootloader was executed
  4034  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 1)
  4035  	m, err := boot.ReadModeenv("")
  4036  	c.Assert(err, IsNil)
  4037  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4038  		"snapd_recovery_mode=run static mocked panic=-1",
  4039  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4040  	})
  4041  	args, err := s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  4042  	c.Assert(err, IsNil)
  4043  	c.Check(args, DeepEquals, map[string]string{
  4044  		"snapd_extra_cmdline_args": "extra args",
  4045  		// canary has been cleared
  4046  		"snapd_full_cmdline_args": "",
  4047  	})
  4048  
  4049  	// REBOOT; since we rebooted after updating the bootenv, the kernel
  4050  	// command line will include arguments that came from gadget snap
  4051  	s.bootloader.SetBootVarsCalls = 0
  4052  	s.resealCalls = 0
  4053  	err = ioutil.WriteFile(cmdlineFile, []byte("snapd_recovery_mode=run static mocked panic=-1 extra args"), 0644)
  4054  	c.Assert(err, IsNil)
  4055  	err = boot.MarkBootSuccessful(s.uc20dev)
  4056  	c.Assert(err, IsNil)
  4057  	// we resealed after reboot again
  4058  	c.Check(s.resealCalls, Equals, 1)
  4059  	// bootenv wasn't touched
  4060  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  4061  	m, err = boot.ReadModeenv("")
  4062  	c.Assert(err, IsNil)
  4063  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4064  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4065  	})
  4066  
  4067  	// try again, as if the task handler gets to run again
  4068  	s.resealCalls = 0
  4069  	reboot, err := boot.UpdateCommandLineForGadgetComponent(s.uc20dev, sf)
  4070  	c.Assert(err, IsNil)
  4071  	// nothing changed now, we already booted with the new command line
  4072  	c.Check(reboot, Equals, false)
  4073  	// not reseal since nothing changed
  4074  	c.Check(s.resealCalls, Equals, 0)
  4075  	// no changes to the bootenv either
  4076  	c.Check(s.bootloader.SetBootVarsCalls, Equals, 0)
  4077  	// all done, modeenv
  4078  	m, err = boot.ReadModeenv("")
  4079  	c.Assert(err, IsNil)
  4080  	c.Check(m.CurrentKernelCommandLines, DeepEquals, boot.BootCommandLines{
  4081  		"snapd_recovery_mode=run static mocked panic=-1 extra args",
  4082  	})
  4083  	args, err = s.bootloader.GetBootVars("snapd_extra_cmdline_args", "snapd_full_cmdline_args")
  4084  	c.Assert(err, IsNil)
  4085  	c.Check(args, DeepEquals, map[string]string{
  4086  		"snapd_extra_cmdline_args": "extra args",
  4087  		"snapd_full_cmdline_args":  "",
  4088  	})
  4089  }