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