github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap-bootstrap/cmd_initramfs_mounts_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019-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 main_test
    21  
    22  import (
    23  	"bytes"
    24  	"encoding/json"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"strings"
    30  	"time"
    31  
    32  	. "gopkg.in/check.v1"
    33  
    34  	"github.com/snapcore/snapd/asserts"
    35  	"github.com/snapcore/snapd/asserts/assertstest"
    36  	"github.com/snapcore/snapd/boot"
    37  	"github.com/snapcore/snapd/boot/boottest"
    38  	"github.com/snapcore/snapd/bootloader"
    39  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    40  	main "github.com/snapcore/snapd/cmd/snap-bootstrap"
    41  	"github.com/snapcore/snapd/dirs"
    42  	"github.com/snapcore/snapd/logger"
    43  	"github.com/snapcore/snapd/osutil"
    44  	"github.com/snapcore/snapd/osutil/disks"
    45  	"github.com/snapcore/snapd/secboot"
    46  	"github.com/snapcore/snapd/seed"
    47  	"github.com/snapcore/snapd/seed/seedtest"
    48  	"github.com/snapcore/snapd/snap"
    49  	"github.com/snapcore/snapd/systemd"
    50  	"github.com/snapcore/snapd/testutil"
    51  )
    52  
    53  var brandPrivKey, _ = assertstest.GenerateKey(752)
    54  
    55  type initramfsMountsSuite struct {
    56  	testutil.BaseTest
    57  
    58  	// makes available a bunch of helper (like MakeAssertedSnap)
    59  	*seedtest.TestingSeed20
    60  
    61  	Stdout *bytes.Buffer
    62  	logs   *bytes.Buffer
    63  
    64  	seedDir  string
    65  	sysLabel string
    66  	model    *asserts.Model
    67  	tmpDir   string
    68  
    69  	snapDeclAssertsTime time.Time
    70  
    71  	kernel   snap.PlaceInfo
    72  	kernelr2 snap.PlaceInfo
    73  	core20   snap.PlaceInfo
    74  	core20r2 snap.PlaceInfo
    75  	snapd    snap.PlaceInfo
    76  }
    77  
    78  var _ = Suite(&initramfsMountsSuite{})
    79  
    80  var (
    81  	tmpfsMountOpts = &main.SystemdMountOptions{
    82  		Tmpfs:  true,
    83  		NoSuid: true,
    84  	}
    85  	needsFsckDiskMountOpts = &main.SystemdMountOptions{
    86  		NeedsFsck: true,
    87  	}
    88  	needsFsckAndNoSuidDiskMountOpts = &main.SystemdMountOptions{
    89  		NeedsFsck: true,
    90  		NoSuid:    true,
    91  	}
    92  	needsNoSuidDiskMountOpts = &main.SystemdMountOptions{
    93  		NoSuid: true,
    94  	}
    95  
    96  	// a boot disk without ubuntu-save
    97  	defaultBootDisk = &disks.MockDiskMapping{
    98  		FilesystemLabelToPartUUID: map[string]string{
    99  			"ubuntu-boot": "ubuntu-boot-partuuid",
   100  			"ubuntu-seed": "ubuntu-seed-partuuid",
   101  			"ubuntu-data": "ubuntu-data-partuuid",
   102  		},
   103  		DiskHasPartitions: true,
   104  		DevNum:            "default",
   105  	}
   106  
   107  	defaultBootWithSaveDisk = &disks.MockDiskMapping{
   108  		FilesystemLabelToPartUUID: map[string]string{
   109  			"ubuntu-boot": "ubuntu-boot-partuuid",
   110  			"ubuntu-seed": "ubuntu-seed-partuuid",
   111  			"ubuntu-data": "ubuntu-data-partuuid",
   112  			"ubuntu-save": "ubuntu-save-partuuid",
   113  		},
   114  		DiskHasPartitions: true,
   115  		DevNum:            "default-with-save",
   116  	}
   117  
   118  	defaultEncBootDisk = &disks.MockDiskMapping{
   119  		FilesystemLabelToPartUUID: map[string]string{
   120  			"ubuntu-boot":     "ubuntu-boot-partuuid",
   121  			"ubuntu-seed":     "ubuntu-seed-partuuid",
   122  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
   123  			"ubuntu-save-enc": "ubuntu-save-enc-partuuid",
   124  		},
   125  		DiskHasPartitions: true,
   126  		DevNum:            "defaultEncDev",
   127  	}
   128  
   129  	mockStateContent = `{"data":{"auth":{"users":[{"id":1,"name":"mvo"}],"macaroon-key":"not-a-cookie","last-id":1}},"some":{"other":"stuff"}}`
   130  )
   131  
   132  func (s *initramfsMountsSuite) setupSeed(c *C, modelAssertTime time.Time, gadgetSnapFiles [][]string) {
   133  	// pretend /run/mnt/ubuntu-seed has a valid seed
   134  	s.seedDir = boot.InitramfsUbuntuSeedDir
   135  
   136  	// now create a minimal uc20 seed dir with snaps/assertions
   137  	seed20 := &seedtest.TestingSeed20{SeedDir: s.seedDir}
   138  	seed20.SetupAssertSigning("canonical")
   139  	restore := seed.MockTrusted(seed20.StoreSigning.Trusted)
   140  	s.AddCleanup(restore)
   141  
   142  	// XXX: we don't really use this but seedtest always expects my-brand
   143  	seed20.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   144  		"verification": "verified",
   145  	})
   146  
   147  	// make sure all the assertions use the same time
   148  	seed20.SetSnapAssertionNow(s.snapDeclAssertsTime)
   149  
   150  	// add a bunch of snaps
   151  	seed20.MakeAssertedSnap(c, "name: snapd\nversion: 1\ntype: snapd", nil, snap.R(1), "canonical", seed20.StoreSigning.Database)
   152  	seed20.MakeAssertedSnap(c, "name: pc\nversion: 1\ntype: gadget\nbase: core20", gadgetSnapFiles, snap.R(1), "canonical", seed20.StoreSigning.Database)
   153  	seed20.MakeAssertedSnap(c, "name: pc-kernel\nversion: 1\ntype: kernel", nil, snap.R(1), "canonical", seed20.StoreSigning.Database)
   154  	seed20.MakeAssertedSnap(c, "name: core20\nversion: 1\ntype: base", nil, snap.R(1), "canonical", seed20.StoreSigning.Database)
   155  
   156  	// pretend that by default, the model uses an older timestamp than the
   157  	// snap assertions
   158  	if modelAssertTime.IsZero() {
   159  		modelAssertTime = s.snapDeclAssertsTime.Add(-30 * time.Minute)
   160  	}
   161  
   162  	s.sysLabel = "20191118"
   163  	s.model = seed20.MakeSeed(c, s.sysLabel, "my-brand", "my-model", map[string]interface{}{
   164  		"display-name": "my model",
   165  		"architecture": "amd64",
   166  		"base":         "core20",
   167  		"timestamp":    modelAssertTime.Format(time.RFC3339),
   168  		"snaps": []interface{}{
   169  			map[string]interface{}{
   170  				"name":            "pc-kernel",
   171  				"id":              seed20.AssertedSnapID("pc-kernel"),
   172  				"type":            "kernel",
   173  				"default-channel": "20",
   174  			},
   175  			map[string]interface{}{
   176  				"name":            "pc",
   177  				"id":              seed20.AssertedSnapID("pc"),
   178  				"type":            "gadget",
   179  				"default-channel": "20",
   180  			}},
   181  	}, nil)
   182  }
   183  
   184  func (s *initramfsMountsSuite) SetUpTest(c *C) {
   185  	s.BaseTest.SetUpTest(c)
   186  
   187  	s.Stdout = bytes.NewBuffer(nil)
   188  
   189  	buf, restore := logger.MockLogger()
   190  	s.AddCleanup(restore)
   191  	s.logs = buf
   192  
   193  	s.tmpDir = c.MkDir()
   194  
   195  	// mock /run/mnt
   196  	dirs.SetRootDir(s.tmpDir)
   197  	restore = func() { dirs.SetRootDir("") }
   198  	s.AddCleanup(restore)
   199  
   200  	// use a specific time for all the assertions, in the future so that we can
   201  	// set the timestamp of the model assertion to something newer than now, but
   202  	// still older than the snap declarations by default
   203  	s.snapDeclAssertsTime = time.Now().Add(60 * time.Minute)
   204  
   205  	// setup the seed
   206  	s.setupSeed(c, time.Time{}, nil)
   207  
   208  	// make test snap PlaceInfo's for various boot functionality
   209  	var err error
   210  	s.kernel, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
   211  	c.Assert(err, IsNil)
   212  
   213  	s.core20, err = snap.ParsePlaceInfoFromSnapFileName("core20_1.snap")
   214  	c.Assert(err, IsNil)
   215  
   216  	s.kernelr2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap")
   217  	c.Assert(err, IsNil)
   218  
   219  	s.core20r2, err = snap.ParsePlaceInfoFromSnapFileName("core20_2.snap")
   220  	c.Assert(err, IsNil)
   221  
   222  	s.snapd, err = snap.ParsePlaceInfoFromSnapFileName("snapd_1.snap")
   223  	c.Assert(err, IsNil)
   224  
   225  	// by default mock that we don't have UEFI vars, etc. to get the booted
   226  	// kernel partition partition uuid
   227  	s.AddCleanup(main.MockPartitionUUIDForBootedKernelDisk(""))
   228  	s.AddCleanup(main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
   229  		return nil
   230  	}))
   231  	s.AddCleanup(main.MockSecbootMeasureSnapModelWhenPossible(func(f func() (*asserts.Model, error)) error {
   232  		c.Check(f, NotNil)
   233  		return nil
   234  	}))
   235  	s.AddCleanup(main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
   236  		return foundUnencrypted(name), nil
   237  	}))
   238  	s.AddCleanup(main.MockSecbootLockSealedKeys(func() error {
   239  		return nil
   240  	}))
   241  
   242  	s.AddCleanup(main.MockOsutilSetTime(func(time.Time) error {
   243  		return nil
   244  	}))
   245  }
   246  
   247  // static test cases for time test variants shared across the different modes
   248  
   249  type timeTestCase struct {
   250  	now          time.Time
   251  	modelTime    time.Time
   252  	expT         time.Time
   253  	setTimeCalls int
   254  	comment      string
   255  }
   256  
   257  func (s *initramfsMountsSuite) timeTestCases() []timeTestCase {
   258  	// epoch time
   259  	epoch := time.Time{}
   260  
   261  	// t1 is the kernel initrd build time
   262  	t1 := s.snapDeclAssertsTime.Add(-30 * 24 * time.Hour)
   263  	// technically there is another time here between t1 and t2, that is the
   264  	// default model sign time, but since it's older than the snap assertion
   265  	// sign time (t2) it's not actually used in the test
   266  
   267  	// t2 is the time that snap-revision / snap-declaration assertions will be
   268  	// signed with
   269  	t2 := s.snapDeclAssertsTime
   270  
   271  	// t3 is a time after the snap-declarations are signed
   272  	t3 := s.snapDeclAssertsTime.Add(30 * 24 * time.Hour)
   273  
   274  	// t4 and t5 are both times after the the snap declarations are signed
   275  	t4 := s.snapDeclAssertsTime.Add(60 * 24 * time.Hour)
   276  	t5 := s.snapDeclAssertsTime.Add(120 * 24 * time.Hour)
   277  
   278  	return []timeTestCase{
   279  		{
   280  			now:          epoch,
   281  			expT:         t2,
   282  			setTimeCalls: 1,
   283  			comment:      "now() is epoch",
   284  		},
   285  		{
   286  			now:          t1,
   287  			expT:         t2,
   288  			setTimeCalls: 1,
   289  			comment:      "now() is kernel initrd sign time",
   290  		},
   291  		{
   292  			now:          t3,
   293  			expT:         t3,
   294  			setTimeCalls: 0,
   295  			comment:      "now() is newer than snap assertion",
   296  		},
   297  		{
   298  			now:          t3,
   299  			modelTime:    t4,
   300  			expT:         t4,
   301  			setTimeCalls: 1,
   302  			comment:      "model time is newer than now(), which is newer than snap asserts",
   303  		},
   304  		{
   305  			now:          t5,
   306  			modelTime:    t4,
   307  			expT:         t5,
   308  			setTimeCalls: 0,
   309  			comment:      "model time is newest, but older than now()",
   310  		},
   311  	}
   312  }
   313  
   314  // helpers to create consistent UnlockResult values
   315  
   316  func foundUnencrypted(name string) secboot.UnlockResult {
   317  	dev := filepath.Join("/dev/disk/by-partuuid", name+"-partuuid")
   318  	return secboot.UnlockResult{
   319  		PartDevice: dev,
   320  		FsDevice:   dev,
   321  	}
   322  }
   323  
   324  func happyUnlocked(name string, method secboot.UnlockMethod) secboot.UnlockResult {
   325  	return secboot.UnlockResult{
   326  		PartDevice:   filepath.Join("/dev/disk/by-partuuid", name+"-enc-partuuid"),
   327  		FsDevice:     filepath.Join("/dev/mapper", name+"-random"),
   328  		IsEncrypted:  true,
   329  		UnlockMethod: method,
   330  	}
   331  }
   332  
   333  func foundEncrypted(name string) secboot.UnlockResult {
   334  	return secboot.UnlockResult{
   335  		PartDevice: filepath.Join("/dev/disk/by-partuuid", name+"-enc-partuuid"),
   336  		// FsDevice is empty if we didn't unlock anything
   337  		FsDevice:    "",
   338  		IsEncrypted: true,
   339  	}
   340  }
   341  
   342  func notFoundPart() secboot.UnlockResult {
   343  	return secboot.UnlockResult{}
   344  }
   345  
   346  // makeSnapFilesOnEarlyBootUbuntuData creates the snap files on ubuntu-data as
   347  // we
   348  func makeSnapFilesOnEarlyBootUbuntuData(c *C, snaps ...snap.PlaceInfo) {
   349  	snapDir := dirs.SnapBlobDirUnder(boot.InitramfsWritableDir)
   350  	err := os.MkdirAll(snapDir, 0755)
   351  	c.Assert(err, IsNil)
   352  	for _, sn := range snaps {
   353  		snFilename := sn.Filename()
   354  		err = ioutil.WriteFile(filepath.Join(snapDir, snFilename), nil, 0644)
   355  		c.Assert(err, IsNil)
   356  	}
   357  }
   358  
   359  func (s *initramfsMountsSuite) mockProcCmdlineContent(c *C, newContent string) {
   360  	mockProcCmdline := filepath.Join(c.MkDir(), "proc-cmdline")
   361  	err := ioutil.WriteFile(mockProcCmdline, []byte(newContent), 0644)
   362  	c.Assert(err, IsNil)
   363  	restore := osutil.MockProcCmdline(mockProcCmdline)
   364  	s.AddCleanup(restore)
   365  }
   366  
   367  func (s *initramfsMountsSuite) mockUbuntuSaveKeyAndMarker(c *C, rootDir, key, marker string) {
   368  	keyPath := filepath.Join(dirs.SnapFDEDirUnder(rootDir), "ubuntu-save.key")
   369  	c.Assert(os.MkdirAll(filepath.Dir(keyPath), 0700), IsNil)
   370  	c.Assert(ioutil.WriteFile(keyPath, []byte(key), 0600), IsNil)
   371  
   372  	if marker != "" {
   373  		markerPath := filepath.Join(dirs.SnapFDEDirUnder(rootDir), "marker")
   374  		c.Assert(ioutil.WriteFile(markerPath, []byte(marker), 0600), IsNil)
   375  	}
   376  }
   377  
   378  func (s *initramfsMountsSuite) mockUbuntuSaveMarker(c *C, rootDir, marker string) {
   379  	markerPath := filepath.Join(rootDir, "device/fde", "marker")
   380  	c.Assert(os.MkdirAll(filepath.Dir(markerPath), 0700), IsNil)
   381  	c.Assert(ioutil.WriteFile(markerPath, []byte(marker), 0600), IsNil)
   382  }
   383  
   384  func (s *initramfsMountsSuite) TestInitramfsMountsNoModeError(c *C) {
   385  	s.mockProcCmdlineContent(c, "nothing-to-see")
   386  
   387  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   388  	c.Assert(err, ErrorMatches, "cannot detect mode nor recovery system to use")
   389  }
   390  
   391  func (s *initramfsMountsSuite) TestInitramfsMountsUnknownMode(c *C) {
   392  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install-foo")
   393  
   394  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   395  	c.Assert(err, ErrorMatches, `cannot use unknown mode "install-foo"`)
   396  }
   397  
   398  type systemdMount struct {
   399  	what  string
   400  	where string
   401  	opts  *main.SystemdMountOptions
   402  }
   403  
   404  // this is a function so we evaluate InitramfsUbuntuBootDir, etc at the time of
   405  // the test to pick up test-specific dirs.GlobalRootDir
   406  func ubuntuLabelMount(label string, mode string) systemdMount {
   407  	mnt := systemdMount{
   408  		opts: needsFsckDiskMountOpts,
   409  	}
   410  	switch label {
   411  	case "ubuntu-boot":
   412  		mnt.what = "/dev/disk/by-label/ubuntu-boot"
   413  		mnt.where = boot.InitramfsUbuntuBootDir
   414  	case "ubuntu-seed":
   415  		mnt.what = "/dev/disk/by-label/ubuntu-seed"
   416  		mnt.where = boot.InitramfsUbuntuSeedDir
   417  		// don't fsck in run mode
   418  		if mode == "run" {
   419  			mnt.opts = nil
   420  		}
   421  	case "ubuntu-data":
   422  		mnt.what = "/dev/disk/by-label/ubuntu-data"
   423  		mnt.where = boot.InitramfsDataDir
   424  		mnt.opts = needsFsckAndNoSuidDiskMountOpts
   425  	}
   426  
   427  	return mnt
   428  }
   429  
   430  // ubuntuPartUUIDMount returns a systemdMount for the partuuid disk, expecting
   431  // that the partuuid contains in it the expected label for easier coding
   432  func ubuntuPartUUIDMount(partuuid string, mode string) systemdMount {
   433  	// all partitions are expected to be mounted with fsck on
   434  	mnt := systemdMount{
   435  		opts: needsFsckDiskMountOpts,
   436  	}
   437  	mnt.what = filepath.Join("/dev/disk/by-partuuid", partuuid)
   438  	switch {
   439  	case strings.Contains(partuuid, "ubuntu-boot"):
   440  		mnt.where = boot.InitramfsUbuntuBootDir
   441  	case strings.Contains(partuuid, "ubuntu-seed"):
   442  		mnt.where = boot.InitramfsUbuntuSeedDir
   443  	case strings.Contains(partuuid, "ubuntu-data"):
   444  		mnt.where = boot.InitramfsDataDir
   445  		mnt.opts = needsFsckAndNoSuidDiskMountOpts
   446  	case strings.Contains(partuuid, "ubuntu-save"):
   447  		mnt.where = boot.InitramfsUbuntuSaveDir
   448  	}
   449  
   450  	return mnt
   451  }
   452  
   453  func (s *initramfsMountsSuite) makeSeedSnapSystemdMount(typ snap.Type) systemdMount {
   454  	mnt := systemdMount{}
   455  	var name, dir string
   456  	switch typ {
   457  	case snap.TypeSnapd:
   458  		name = "snapd"
   459  		dir = "snapd"
   460  	case snap.TypeBase:
   461  		name = "core20"
   462  		dir = "base"
   463  	case snap.TypeKernel:
   464  		name = "pc-kernel"
   465  		dir = "kernel"
   466  	}
   467  	mnt.what = filepath.Join(s.seedDir, "snaps", name+"_1.snap")
   468  	mnt.where = filepath.Join(boot.InitramfsRunMntDir, dir)
   469  
   470  	return mnt
   471  }
   472  
   473  func (s *initramfsMountsSuite) makeRunSnapSystemdMount(typ snap.Type, sn snap.PlaceInfo) systemdMount {
   474  	mnt := systemdMount{}
   475  	var dir string
   476  	switch typ {
   477  	case snap.TypeSnapd:
   478  		dir = "snapd"
   479  	case snap.TypeBase:
   480  		dir = "base"
   481  	case snap.TypeKernel:
   482  		dir = "kernel"
   483  	}
   484  
   485  	mnt.what = filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), sn.Filename())
   486  	mnt.where = filepath.Join(boot.InitramfsRunMntDir, dir)
   487  
   488  	return mnt
   489  }
   490  
   491  func (s *initramfsMountsSuite) mockSystemdMountSequence(c *C, mounts []systemdMount, comment CommentInterface) (restore func()) {
   492  	n := 0
   493  	if comment == nil {
   494  		comment = Commentf("")
   495  	}
   496  	s.AddCleanup(func() {
   497  		// make sure that after the test is done, we had as many mount calls as
   498  		// mocked mounts
   499  		c.Check(n, Equals, len(mounts), comment)
   500  	})
   501  	return main.MockSystemdMount(func(what, where string, opts *main.SystemdMountOptions) error {
   502  		n++
   503  		c.Assert(n <= len(mounts), Equals, true)
   504  		if n > len(mounts) {
   505  			return fmt.Errorf("unexpected systemd-mount call: %s, %s, %+v", what, where, opts)
   506  		}
   507  		mnt := mounts[n-1]
   508  		c.Assert(what, Equals, mnt.what, comment)
   509  		c.Assert(where, Equals, mnt.where, comment)
   510  		c.Assert(opts, DeepEquals, mnt.opts, Commentf("what is %s, where is %s, comment is %s", what, where, comment))
   511  		return nil
   512  	})
   513  }
   514  
   515  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeHappy(c *C) {
   516  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   517  
   518  	// ensure that we check that access to sealed keys were locked
   519  	sealedKeysLocked := false
   520  	defer main.MockSecbootLockSealedKeys(func() error {
   521  		sealedKeysLocked = true
   522  		return nil
   523  	})()
   524  
   525  	restore := s.mockSystemdMountSequence(c, []systemdMount{
   526  		ubuntuLabelMount("ubuntu-seed", "install"),
   527  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   528  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
   529  		s.makeSeedSnapSystemdMount(snap.TypeBase),
   530  		{
   531  			"tmpfs",
   532  			boot.InitramfsDataDir,
   533  			tmpfsMountOpts,
   534  		},
   535  	}, nil)
   536  	defer restore()
   537  
   538  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   539  	c.Assert(err, IsNil)
   540  
   541  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
   542  	c.Check(modeEnv, testutil.FileEquals, `mode=install
   543  recovery_system=20191118
   544  base=core20_1.snap
   545  model=my-brand/my-model
   546  grade=signed
   547  `)
   548  	cloudInitDisable := filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/cloud/cloud-init.disabled")
   549  	c.Check(cloudInitDisable, testutil.FilePresent)
   550  
   551  	c.Check(sealedKeysLocked, Equals, true)
   552  }
   553  
   554  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeBootFlagsSet(c *C) {
   555  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   556  
   557  	tt := []struct {
   558  		bootFlags        string
   559  		expBootFlagsFile string
   560  	}{
   561  		{
   562  			"factory",
   563  			"factory",
   564  		},
   565  		{
   566  			"factory,,,,",
   567  			"factory",
   568  		},
   569  		{
   570  			"factory,,,,unknown-new-flag",
   571  			"factory,unknown-new-flag",
   572  		},
   573  		{
   574  			"",
   575  			"",
   576  		},
   577  	}
   578  
   579  	for _, t := range tt {
   580  		restore := s.mockSystemdMountSequence(c, []systemdMount{
   581  			ubuntuLabelMount("ubuntu-seed", "install"),
   582  			s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   583  			s.makeSeedSnapSystemdMount(snap.TypeKernel),
   584  			s.makeSeedSnapSystemdMount(snap.TypeBase),
   585  			{
   586  				"tmpfs",
   587  				boot.InitramfsDataDir,
   588  				tmpfsMountOpts,
   589  			},
   590  		}, nil)
   591  		defer restore()
   592  
   593  		// mock a bootloader
   594  		bl := bootloadertest.Mock("bootloader", c.MkDir())
   595  		err := bl.SetBootVars(map[string]string{
   596  			"snapd_boot_flags": t.bootFlags,
   597  		})
   598  		c.Assert(err, IsNil)
   599  		bootloader.Force(bl)
   600  		defer bootloader.Force(nil)
   601  
   602  		_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   603  		c.Assert(err, IsNil)
   604  
   605  		// check that we wrote the /run file with the boot flags in it
   606  		c.Assert(filepath.Join(dirs.SnapRunDir, "boot-flags"), testutil.FileEquals, t.expBootFlagsFile)
   607  	}
   608  }
   609  
   610  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeBootFlagsSet(c *C) {
   611  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   612  
   613  	tt := []struct {
   614  		bootFlags        []string
   615  		expBootFlagsFile string
   616  	}{
   617  		{
   618  			[]string{"factory"},
   619  			"factory",
   620  		},
   621  		{
   622  			[]string{"factory", ""},
   623  			"factory",
   624  		},
   625  		{
   626  			[]string{"factory", "unknown-new-flag"},
   627  			"factory,unknown-new-flag",
   628  		},
   629  		{
   630  			[]string{},
   631  			"",
   632  		},
   633  	}
   634  
   635  	for _, t := range tt {
   636  		restore := disks.MockMountPointDisksToPartitionMapping(
   637  			map[disks.Mountpoint]*disks.MockDiskMapping{
   638  				{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootWithSaveDisk,
   639  				{Mountpoint: boot.InitramfsDataDir}:       defaultBootWithSaveDisk,
   640  				{Mountpoint: boot.InitramfsUbuntuSaveDir}: defaultBootWithSaveDisk,
   641  			},
   642  		)
   643  		defer restore()
   644  
   645  		restore = s.mockSystemdMountSequence(c, []systemdMount{
   646  			ubuntuLabelMount("ubuntu-boot", "run"),
   647  			ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
   648  			ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
   649  			ubuntuPartUUIDMount("ubuntu-save-partuuid", "run"),
   650  			s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
   651  			s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
   652  		}, nil)
   653  		defer restore()
   654  
   655  		// mock a bootloader
   656  		bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   657  		bootloader.Force(bloader)
   658  		defer bootloader.Force(nil)
   659  
   660  		// set the current kernel
   661  		restore = bloader.SetEnabledKernel(s.kernel)
   662  		defer restore()
   663  
   664  		makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   665  
   666  		// write modeenv with boot flags
   667  		modeEnv := boot.Modeenv{
   668  			Mode:           "run",
   669  			Base:           s.core20.Filename(),
   670  			CurrentKernels: []string{s.kernel.Filename()},
   671  			BootFlags:      t.bootFlags,
   672  		}
   673  		err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   674  		c.Assert(err, IsNil)
   675  
   676  		_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   677  		c.Assert(err, IsNil)
   678  
   679  		// check that we wrote the /run file with the boot flags in it
   680  		c.Assert(filepath.Join(dirs.SnapRunDir, "boot-flags"), testutil.FileEquals, t.expBootFlagsFile)
   681  	}
   682  }
   683  
   684  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeTimeMovesForwardHappy(c *C) {
   685  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   686  
   687  	for _, tc := range s.timeTestCases() {
   688  		comment := Commentf(tc.comment)
   689  		cleanups := []func(){}
   690  
   691  		// always remove the ubuntu-seed dir, otherwise setupSeed complains the
   692  		// model file already exists and can't setup the seed
   693  		err := os.RemoveAll(filepath.Join(boot.InitramfsUbuntuSeedDir))
   694  		c.Assert(err, IsNil, comment)
   695  		s.setupSeed(c, tc.modelTime, nil)
   696  
   697  		restore := main.MockTimeNow(func() time.Time {
   698  			return tc.now
   699  		})
   700  		cleanups = append(cleanups, restore)
   701  		osutilSetTimeCalls := 0
   702  
   703  		// check what time we try to move forward to
   704  		restore = main.MockOsutilSetTime(func(t time.Time) error {
   705  			osutilSetTimeCalls++
   706  			// make sure the timestamps are within 1 second of each other, they
   707  			// won't be equal since the timestamp is serialized to an assertion and
   708  			// read back
   709  			tTrunc := t.Truncate(2 * time.Second)
   710  			expTTrunc := tc.expT.Truncate(2 * time.Second)
   711  			c.Assert(tTrunc.Equal(expTTrunc), Equals, true, Commentf("%s, exp %s, got %s", tc.comment, t, s.snapDeclAssertsTime))
   712  			return nil
   713  		})
   714  		cleanups = append(cleanups, restore)
   715  
   716  		restore = s.mockSystemdMountSequence(c, []systemdMount{
   717  			ubuntuLabelMount("ubuntu-seed", "install"),
   718  			s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   719  			s.makeSeedSnapSystemdMount(snap.TypeKernel),
   720  			s.makeSeedSnapSystemdMount(snap.TypeBase),
   721  			{
   722  				"tmpfs",
   723  				boot.InitramfsDataDir,
   724  				tmpfsMountOpts,
   725  			},
   726  		}, nil)
   727  		cleanups = append(cleanups, restore)
   728  
   729  		_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   730  		c.Assert(err, IsNil, comment)
   731  
   732  		c.Assert(osutilSetTimeCalls, Equals, tc.setTimeCalls)
   733  
   734  		for _, r := range cleanups {
   735  			r()
   736  		}
   737  	}
   738  }
   739  
   740  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeGadgetDefaultsHappy(c *C) {
   741  	// setup a seed with default gadget yaml
   742  	const gadgetYamlDefaults = `
   743  defaults:
   744    system:
   745      service:
   746        rsyslog.disable: true
   747        ssh.disable: true
   748        console-conf.disable: true
   749      journal.persistent: true
   750  `
   751  	c.Assert(os.RemoveAll(s.seedDir), IsNil)
   752  
   753  	s.setupSeed(c, time.Time{}, [][]string{
   754  		{"meta/gadget.yaml", gadgetYamlDefaults},
   755  	})
   756  
   757  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   758  
   759  	restore := s.mockSystemdMountSequence(c, []systemdMount{
   760  		ubuntuLabelMount("ubuntu-seed", "install"),
   761  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   762  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
   763  		s.makeSeedSnapSystemdMount(snap.TypeBase),
   764  		{
   765  			"tmpfs",
   766  			boot.InitramfsDataDir,
   767  			tmpfsMountOpts,
   768  		},
   769  	}, nil)
   770  	defer restore()
   771  
   772  	// we will call out to systemctl in the initramfs, but only using --root
   773  	// which doesn't talk to systemd, just manipulates files around
   774  	var sysctlArgs [][]string
   775  	systemctlRestorer := systemd.MockSystemctl(func(args ...string) (buf []byte, err error) {
   776  		sysctlArgs = append(sysctlArgs, args)
   777  		return nil, nil
   778  	})
   779  	defer systemctlRestorer()
   780  
   781  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   782  	c.Assert(err, IsNil)
   783  
   784  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
   785  	c.Check(modeEnv, testutil.FileEquals, `mode=install
   786  recovery_system=20191118
   787  base=core20_1.snap
   788  model=my-brand/my-model
   789  grade=signed
   790  `)
   791  
   792  	cloudInitDisable := filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/cloud/cloud-init.disabled")
   793  	c.Check(cloudInitDisable, testutil.FilePresent)
   794  
   795  	// check that everything from the gadget defaults was setup
   796  	c.Assert(osutil.FileExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/ssh/sshd_not_to_be_run")), Equals, true)
   797  	c.Assert(osutil.FileExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/var/lib/console-conf/complete")), Equals, true)
   798  	exists, _, _ := osutil.DirExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/var/log/journal"))
   799  	c.Assert(exists, Equals, true)
   800  
   801  	// systemctl was called the way we expect
   802  	c.Assert(sysctlArgs, DeepEquals, [][]string{{"--root", filepath.Join(boot.InitramfsWritableDir, "_writable_defaults"), "mask", "rsyslog.service"}})
   803  }
   804  
   805  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeBootedKernelPartitionUUIDHappy(c *C) {
   806  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   807  
   808  	restore := main.MockPartitionUUIDForBootedKernelDisk("specific-ubuntu-seed-partuuid")
   809  	defer restore()
   810  
   811  	restore = s.mockSystemdMountSequence(c, []systemdMount{
   812  		{
   813  			"/dev/disk/by-partuuid/specific-ubuntu-seed-partuuid",
   814  			boot.InitramfsUbuntuSeedDir,
   815  			needsFsckDiskMountOpts,
   816  		},
   817  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   818  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
   819  		s.makeSeedSnapSystemdMount(snap.TypeBase),
   820  		{
   821  			"tmpfs",
   822  			boot.InitramfsDataDir,
   823  			tmpfsMountOpts,
   824  		},
   825  	}, nil)
   826  	defer restore()
   827  
   828  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   829  	c.Assert(err, IsNil)
   830  
   831  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
   832  	c.Check(modeEnv, testutil.FileEquals, `mode=install
   833  recovery_system=20191118
   834  base=core20_1.snap
   835  model=my-brand/my-model
   836  grade=signed
   837  `)
   838  	cloudInitDisable := filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/cloud/cloud-init.disabled")
   839  	c.Check(cloudInitDisable, testutil.FilePresent)
   840  }
   841  
   842  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeUnencryptedWithSaveHappy(c *C) {
   843  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   844  
   845  	restore := disks.MockMountPointDisksToPartitionMapping(
   846  		map[disks.Mountpoint]*disks.MockDiskMapping{
   847  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootWithSaveDisk,
   848  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootWithSaveDisk,
   849  			{Mountpoint: boot.InitramfsUbuntuSaveDir}: defaultBootWithSaveDisk,
   850  		},
   851  	)
   852  	defer restore()
   853  
   854  	restore = s.mockSystemdMountSequence(c, []systemdMount{
   855  		ubuntuLabelMount("ubuntu-boot", "run"),
   856  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
   857  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
   858  		ubuntuPartUUIDMount("ubuntu-save-partuuid", "run"),
   859  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
   860  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
   861  	}, nil)
   862  	defer restore()
   863  
   864  	// mock a bootloader
   865  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   866  	bootloader.Force(bloader)
   867  	defer bootloader.Force(nil)
   868  
   869  	// set the current kernel
   870  	restore = bloader.SetEnabledKernel(s.kernel)
   871  	defer restore()
   872  
   873  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   874  
   875  	// write modeenv
   876  	modeEnv := boot.Modeenv{
   877  		Mode:           "run",
   878  		Base:           s.core20.Filename(),
   879  		CurrentKernels: []string{s.kernel.Filename()},
   880  	}
   881  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   882  	c.Assert(err, IsNil)
   883  
   884  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   885  	c.Assert(err, IsNil)
   886  }
   887  
   888  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeTimeMovesForwardHappy(c *C) {
   889  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   890  
   891  	for _, isFirstBoot := range []bool{true, false} {
   892  		for _, tc := range s.timeTestCases() {
   893  			comment := Commentf(tc.comment)
   894  			cleanups := []func(){}
   895  
   896  			// always remove the ubuntu-seed dir, otherwise setupSeed complains the
   897  			// model file already exists and can't setup the seed
   898  			err := os.RemoveAll(filepath.Join(boot.InitramfsUbuntuSeedDir))
   899  			c.Assert(err, IsNil, comment)
   900  			s.setupSeed(c, tc.modelTime, nil)
   901  
   902  			restore := main.MockTimeNow(func() time.Time {
   903  				return tc.now
   904  			})
   905  			cleanups = append(cleanups, restore)
   906  
   907  			restore = disks.MockMountPointDisksToPartitionMapping(
   908  				map[disks.Mountpoint]*disks.MockDiskMapping{
   909  					{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
   910  					{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
   911  				},
   912  			)
   913  			cleanups = append(cleanups, restore)
   914  
   915  			osutilSetTimeCalls := 0
   916  
   917  			// check what time we try to move forward to
   918  			restore = main.MockOsutilSetTime(func(t time.Time) error {
   919  				osutilSetTimeCalls++
   920  				// make sure the timestamps are within 1 second of each other, they
   921  				// won't be equal since the timestamp is serialized to an assertion and
   922  				// read back
   923  				tTrunc := t.Truncate(2 * time.Second)
   924  				expTTrunc := tc.expT.Truncate(2 * time.Second)
   925  				c.Assert(tTrunc.Equal(expTTrunc), Equals, true, Commentf("%s, exp %s, got %s", tc.comment, t, s.snapDeclAssertsTime))
   926  				return nil
   927  			})
   928  			cleanups = append(cleanups, restore)
   929  
   930  			mnts := []systemdMount{
   931  				ubuntuLabelMount("ubuntu-boot", "run"),
   932  				ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
   933  				ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
   934  				s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
   935  				s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
   936  			}
   937  
   938  			if isFirstBoot {
   939  				mnts = append(mnts, s.makeSeedSnapSystemdMount(snap.TypeSnapd))
   940  			}
   941  
   942  			restore = s.mockSystemdMountSequence(c, mnts, nil)
   943  			cleanups = append(cleanups, restore)
   944  
   945  			// mock a bootloader
   946  			bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   947  			bootloader.Force(bloader)
   948  			cleanups = append(cleanups, func() { bootloader.Force(nil) })
   949  
   950  			// set the current kernel
   951  			restore = bloader.SetEnabledKernel(s.kernel)
   952  			cleanups = append(cleanups, restore)
   953  
   954  			makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   955  
   956  			// write modeenv
   957  			modeEnv := boot.Modeenv{
   958  				Mode:           "run",
   959  				Base:           s.core20.Filename(),
   960  				CurrentKernels: []string{s.kernel.Filename()},
   961  			}
   962  
   963  			if isFirstBoot {
   964  				// set RecoverySystem so that the system operates in first boot
   965  				// of run mode, and still reads the system essential snaps to
   966  				// mount the snapd snap
   967  				modeEnv.RecoverySystem = "20191118"
   968  			}
   969  
   970  			err = modeEnv.WriteTo(boot.InitramfsWritableDir)
   971  			c.Assert(err, IsNil, comment)
   972  
   973  			_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   974  			c.Assert(err, IsNil, comment)
   975  
   976  			if isFirstBoot {
   977  				c.Assert(osutilSetTimeCalls, Equals, tc.setTimeCalls, comment)
   978  			} else {
   979  				// non-first boot should not have moved the time at all since it
   980  				// doesn't read assertions
   981  				c.Assert(osutilSetTimeCalls, Equals, 0, comment)
   982  			}
   983  
   984  			for _, r := range cleanups {
   985  				r()
   986  			}
   987  		}
   988  
   989  	}
   990  }
   991  
   992  func (s *initramfsMountsSuite) testInitramfsMountsRunModeNoSaveUnencrypted(c *C) error {
   993  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   994  
   995  	restore := disks.MockMountPointDisksToPartitionMapping(
   996  		map[disks.Mountpoint]*disks.MockDiskMapping{
   997  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
   998  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
   999  		},
  1000  	)
  1001  	defer restore()
  1002  
  1003  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1004  		ubuntuLabelMount("ubuntu-boot", "run"),
  1005  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1006  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
  1007  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1008  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1009  	}, nil)
  1010  	defer restore()
  1011  
  1012  	// mock a bootloader
  1013  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1014  	bootloader.Force(bloader)
  1015  	defer bootloader.Force(nil)
  1016  
  1017  	// set the current kernel
  1018  	restore = bloader.SetEnabledKernel(s.kernel)
  1019  	defer restore()
  1020  
  1021  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1022  
  1023  	// write modeenv
  1024  	modeEnv := boot.Modeenv{
  1025  		Mode:           "run",
  1026  		Base:           s.core20.Filename(),
  1027  		CurrentKernels: []string{s.kernel.Filename()},
  1028  	}
  1029  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1030  	c.Assert(err, IsNil)
  1031  
  1032  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1033  	return err
  1034  }
  1035  
  1036  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeNoSaveUnencryptedHappy(c *C) {
  1037  	// ensure that we check that access to sealed keys were locked
  1038  	sealedKeysLocked := false
  1039  	defer main.MockSecbootLockSealedKeys(func() error {
  1040  		sealedKeysLocked = true
  1041  		return nil
  1042  	})()
  1043  
  1044  	err := s.testInitramfsMountsRunModeNoSaveUnencrypted(c)
  1045  	c.Assert(err, IsNil)
  1046  
  1047  	c.Check(sealedKeysLocked, Equals, true)
  1048  }
  1049  
  1050  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeNoSaveUnencryptedKeyLockingUnhappy(c *C) {
  1051  	// have blocking sealed keys fail
  1052  	defer main.MockSecbootLockSealedKeys(func() error {
  1053  		return fmt.Errorf("blocking keys failed")
  1054  	})()
  1055  
  1056  	err := s.testInitramfsMountsRunModeNoSaveUnencrypted(c)
  1057  	c.Assert(err, ErrorMatches, "error locking access to sealed keys: blocking keys failed")
  1058  }
  1059  
  1060  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeRealSystemdMountTimesOutNoMount(c *C) {
  1061  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
  1062  
  1063  	testStart := time.Now()
  1064  	timeCalls := 0
  1065  	restore := main.MockTimeNow(func() time.Time {
  1066  		timeCalls++
  1067  		switch timeCalls {
  1068  		case 1, 2:
  1069  			return testStart
  1070  		case 3:
  1071  			// 1:31 later, we should time out
  1072  			return testStart.Add(1*time.Minute + 31*time.Second)
  1073  		default:
  1074  			c.Errorf("unexpected time.Now() call (%d)", timeCalls)
  1075  			// we want the test to fail at some point and not run forever, so
  1076  			// move time way forward to make it for sure time out
  1077  			return testStart.Add(10000 * time.Hour)
  1078  		}
  1079  	})
  1080  	defer restore()
  1081  
  1082  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
  1083  	defer cmd.Restore()
  1084  
  1085  	isMountedCalls := 0
  1086  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
  1087  		isMountedCalls++
  1088  		switch isMountedCalls {
  1089  		// always return false for the mount
  1090  		case 1, 2:
  1091  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
  1092  			return false, nil
  1093  		default:
  1094  			// shouldn't be called more than twice due to the time.Now() mocking
  1095  			c.Errorf("test broken, IsMounted called too many (%d) times", isMountedCalls)
  1096  			return false, fmt.Errorf("test broken, IsMounted called too many (%d) times", isMountedCalls)
  1097  		}
  1098  	})
  1099  	defer restore()
  1100  
  1101  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1102  	c.Assert(err, ErrorMatches, fmt.Sprintf("timed out after 1m30s waiting for mount %s on %s", "/dev/disk/by-label/ubuntu-seed", boot.InitramfsUbuntuSeedDir))
  1103  	c.Check(s.Stdout.String(), Equals, "")
  1104  
  1105  }
  1106  
  1107  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeHappyRealSystemdMount(c *C) {
  1108  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
  1109  
  1110  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
  1111  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
  1112  	snapdMnt := filepath.Join(boot.InitramfsRunMntDir, "snapd")
  1113  
  1114  	// don't do anything from systemd-mount, we verify the arguments passed at
  1115  	// the end with cmd.Calls
  1116  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
  1117  	defer cmd.Restore()
  1118  
  1119  	// mock that in turn, /run/mnt/ubuntu-boot, /run/mnt/ubuntu-seed, etc. are
  1120  	// mounted
  1121  	n := 0
  1122  	restore := main.MockOsutilIsMounted(func(where string) (bool, error) {
  1123  		n++
  1124  		switch n {
  1125  		// first call for each mount returns false, then returns true, this
  1126  		// tests in the case where systemd is racy / inconsistent and things
  1127  		// aren't mounted by the time systemd-mount returns
  1128  		case 1, 2:
  1129  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
  1130  			return n%2 == 0, nil
  1131  		case 3, 4:
  1132  			c.Assert(where, Equals, snapdMnt)
  1133  			return n%2 == 0, nil
  1134  		case 5, 6:
  1135  			c.Assert(where, Equals, kernelMnt)
  1136  			return n%2 == 0, nil
  1137  		case 7, 8:
  1138  			c.Assert(where, Equals, baseMnt)
  1139  			return n%2 == 0, nil
  1140  		case 9, 10:
  1141  			c.Assert(where, Equals, boot.InitramfsDataDir)
  1142  			return n%2 == 0, nil
  1143  		default:
  1144  			c.Errorf("unexpected IsMounted check on %s", where)
  1145  			return false, fmt.Errorf("unexpected IsMounted check on %s", where)
  1146  		}
  1147  	})
  1148  	defer restore()
  1149  
  1150  	// mock a bootloader
  1151  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1152  	bootloader.Force(bloader)
  1153  	defer bootloader.Force(nil)
  1154  
  1155  	// set the current kernel
  1156  	restore = bloader.SetEnabledKernel(s.kernel)
  1157  	defer restore()
  1158  
  1159  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1160  
  1161  	// write modeenv
  1162  	modeEnv := boot.Modeenv{
  1163  		Mode:           "run",
  1164  		Base:           s.core20.Filename(),
  1165  		CurrentKernels: []string{s.kernel.Filename()},
  1166  	}
  1167  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1168  	c.Assert(err, IsNil)
  1169  
  1170  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1171  	c.Assert(err, IsNil)
  1172  	c.Check(s.Stdout.String(), Equals, "")
  1173  
  1174  	// check that all of the override files are present
  1175  	for _, initrdUnit := range []string{
  1176  		"initrd.target",
  1177  		"initrd-fs.target",
  1178  		"initrd-switch-root.target",
  1179  		"local-fs.target",
  1180  	} {
  1181  		for _, mountUnit := range []string{
  1182  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSeedDir),
  1183  			systemd.EscapeUnitNamePath(snapdMnt),
  1184  			systemd.EscapeUnitNamePath(kernelMnt),
  1185  			systemd.EscapeUnitNamePath(baseMnt),
  1186  			systemd.EscapeUnitNamePath(boot.InitramfsDataDir),
  1187  		} {
  1188  			fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
  1189  			unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
  1190  			c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
  1191  Requires=%[1]s
  1192  After=%[1]s
  1193  `, mountUnit+".mount"))
  1194  		}
  1195  	}
  1196  
  1197  	// 2 IsMounted calls per mount point, so 10 total IsMounted calls
  1198  	c.Assert(n, Equals, 10)
  1199  
  1200  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
  1201  		{
  1202  			"systemd-mount",
  1203  			"/dev/disk/by-label/ubuntu-seed",
  1204  			boot.InitramfsUbuntuSeedDir,
  1205  			"--no-pager",
  1206  			"--no-ask-password",
  1207  			"--fsck=yes",
  1208  		}, {
  1209  			"systemd-mount",
  1210  			filepath.Join(s.seedDir, "snaps", s.snapd.Filename()),
  1211  			snapdMnt,
  1212  			"--no-pager",
  1213  			"--no-ask-password",
  1214  			"--fsck=no",
  1215  		}, {
  1216  			"systemd-mount",
  1217  			filepath.Join(s.seedDir, "snaps", s.kernel.Filename()),
  1218  			kernelMnt,
  1219  			"--no-pager",
  1220  			"--no-ask-password",
  1221  			"--fsck=no",
  1222  		}, {
  1223  			"systemd-mount",
  1224  			filepath.Join(s.seedDir, "snaps", s.core20.Filename()),
  1225  			baseMnt,
  1226  			"--no-pager",
  1227  			"--no-ask-password",
  1228  			"--fsck=no",
  1229  		}, {
  1230  			"systemd-mount",
  1231  			"tmpfs",
  1232  			boot.InitramfsDataDir,
  1233  			"--no-pager",
  1234  			"--no-ask-password",
  1235  			"--type=tmpfs",
  1236  			"--fsck=no",
  1237  			"--options=nosuid",
  1238  		},
  1239  	})
  1240  }
  1241  
  1242  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeNoSaveHappyRealSystemdMount(c *C) {
  1243  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  1244  
  1245  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
  1246  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
  1247  	snapdMnt := filepath.Join(boot.InitramfsRunMntDir, "snapd")
  1248  
  1249  	restore := disks.MockMountPointDisksToPartitionMapping(
  1250  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1251  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootDisk,
  1252  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootDisk,
  1253  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootDisk,
  1254  		},
  1255  	)
  1256  	defer restore()
  1257  
  1258  	// don't do anything from systemd-mount, we verify the arguments passed at
  1259  	// the end with cmd.Calls
  1260  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
  1261  	defer cmd.Restore()
  1262  
  1263  	// mock that in turn, /run/mnt/ubuntu-boot, /run/mnt/ubuntu-seed, etc. are
  1264  	// mounted
  1265  	n := 0
  1266  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
  1267  		n++
  1268  		switch n {
  1269  		// first call for each mount returns false, then returns true, this
  1270  		// tests in the case where systemd is racy / inconsistent and things
  1271  		// aren't mounted by the time systemd-mount returns
  1272  		case 1, 2:
  1273  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
  1274  			return n%2 == 0, nil
  1275  		case 3, 4:
  1276  			c.Assert(where, Equals, snapdMnt)
  1277  			return n%2 == 0, nil
  1278  		case 5, 6:
  1279  			c.Assert(where, Equals, kernelMnt)
  1280  			return n%2 == 0, nil
  1281  		case 7, 8:
  1282  			c.Assert(where, Equals, baseMnt)
  1283  			return n%2 == 0, nil
  1284  		case 9, 10:
  1285  			c.Assert(where, Equals, boot.InitramfsDataDir)
  1286  			return n%2 == 0, nil
  1287  		case 11, 12:
  1288  			c.Assert(where, Equals, boot.InitramfsUbuntuBootDir)
  1289  			return n%2 == 0, nil
  1290  		case 13, 14:
  1291  			c.Assert(where, Equals, boot.InitramfsHostUbuntuDataDir)
  1292  			return n%2 == 0, nil
  1293  		default:
  1294  			c.Errorf("unexpected IsMounted check on %s", where)
  1295  			return false, fmt.Errorf("unexpected IsMounted check on %s", where)
  1296  		}
  1297  	})
  1298  	defer restore()
  1299  
  1300  	unlockVolumeWithSealedKeyCalls := 0
  1301  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  1302  		// this test doesn't use ubuntu-save, so we need to return an
  1303  		// unencrypted ubuntu-data the first time, but not found the second time
  1304  		unlockVolumeWithSealedKeyCalls++
  1305  		switch unlockVolumeWithSealedKeyCalls {
  1306  		case 1:
  1307  			return foundUnencrypted(name), nil
  1308  		case 2:
  1309  			return notFoundPart(), fmt.Errorf("error enumerating to find ubuntu-save")
  1310  		default:
  1311  			c.Errorf("unexpected call (number %d) to UnlockVolumeUsingSealedKeyIfEncrypted", unlockVolumeWithSealedKeyCalls)
  1312  			return secboot.UnlockResult{}, fmt.Errorf("unexpected call (%d) to UnlockVolumeUsingSealedKeyIfEncrypted", unlockVolumeWithSealedKeyCalls)
  1313  		}
  1314  	})
  1315  	defer restore()
  1316  
  1317  	// mock a bootloader
  1318  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1319  	bootloader.Force(bloader)
  1320  	defer bootloader.Force(nil)
  1321  
  1322  	// set the current kernel
  1323  	restore = bloader.SetEnabledKernel(s.kernel)
  1324  	defer restore()
  1325  
  1326  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1327  
  1328  	// write modeenv
  1329  	modeEnv := boot.Modeenv{
  1330  		Mode:           "run",
  1331  		Base:           s.core20.Filename(),
  1332  		CurrentKernels: []string{s.kernel.Filename()},
  1333  	}
  1334  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1335  	c.Assert(err, IsNil)
  1336  
  1337  	s.testRecoverModeHappy(c)
  1338  
  1339  	c.Check(s.Stdout.String(), Equals, "")
  1340  
  1341  	// check that all of the override files are present
  1342  	for _, initrdUnit := range []string{
  1343  		"initrd.target",
  1344  		"initrd-fs.target",
  1345  		"initrd-switch-root.target",
  1346  		"local-fs.target",
  1347  	} {
  1348  		for _, mountUnit := range []string{
  1349  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSeedDir),
  1350  			systemd.EscapeUnitNamePath(snapdMnt),
  1351  			systemd.EscapeUnitNamePath(kernelMnt),
  1352  			systemd.EscapeUnitNamePath(baseMnt),
  1353  			systemd.EscapeUnitNamePath(boot.InitramfsDataDir),
  1354  			systemd.EscapeUnitNamePath(boot.InitramfsHostUbuntuDataDir),
  1355  		} {
  1356  			fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
  1357  			unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
  1358  			c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
  1359  Requires=%[1]s
  1360  After=%[1]s
  1361  `, mountUnit+".mount"))
  1362  		}
  1363  	}
  1364  
  1365  	// 2 IsMounted calls per mount point, so 14 total IsMounted calls
  1366  	c.Assert(n, Equals, 14)
  1367  
  1368  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
  1369  		{
  1370  			"systemd-mount",
  1371  			"/dev/disk/by-label/ubuntu-seed",
  1372  			boot.InitramfsUbuntuSeedDir,
  1373  			"--no-pager",
  1374  			"--no-ask-password",
  1375  			"--fsck=yes",
  1376  		}, {
  1377  			"systemd-mount",
  1378  			filepath.Join(s.seedDir, "snaps", s.snapd.Filename()),
  1379  			snapdMnt,
  1380  			"--no-pager",
  1381  			"--no-ask-password",
  1382  			"--fsck=no",
  1383  		}, {
  1384  			"systemd-mount",
  1385  			filepath.Join(s.seedDir, "snaps", s.kernel.Filename()),
  1386  			kernelMnt,
  1387  			"--no-pager",
  1388  			"--no-ask-password",
  1389  			"--fsck=no",
  1390  		}, {
  1391  			"systemd-mount",
  1392  			filepath.Join(s.seedDir, "snaps", s.core20.Filename()),
  1393  			baseMnt,
  1394  			"--no-pager",
  1395  			"--no-ask-password",
  1396  			"--fsck=no",
  1397  		}, {
  1398  			"systemd-mount",
  1399  			"tmpfs",
  1400  			boot.InitramfsDataDir,
  1401  			"--no-pager",
  1402  			"--no-ask-password",
  1403  			"--type=tmpfs",
  1404  			"--fsck=no",
  1405  			"--options=nosuid",
  1406  		}, {
  1407  			"systemd-mount",
  1408  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  1409  			boot.InitramfsUbuntuBootDir,
  1410  			"--no-pager",
  1411  			"--no-ask-password",
  1412  			"--fsck=yes",
  1413  		}, {
  1414  			"systemd-mount",
  1415  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1416  			boot.InitramfsHostUbuntuDataDir,
  1417  			"--no-pager",
  1418  			"--no-ask-password",
  1419  			"--fsck=no",
  1420  			"--options=nosuid",
  1421  		},
  1422  	})
  1423  
  1424  	// we should not have written a degraded.json
  1425  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  1426  
  1427  	// we should have only tried to unseal things only once, when unlocking ubuntu-data
  1428  	c.Assert(unlockVolumeWithSealedKeyCalls, Equals, 1)
  1429  
  1430  	// save is optional and not found in this test
  1431  	c.Check(s.logs.String(), testutil.Contains, "ubuntu-save was not found")
  1432  }
  1433  
  1434  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeWithSaveHappyRealSystemdMount(c *C) {
  1435  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  1436  
  1437  	restore := disks.MockMountPointDisksToPartitionMapping(
  1438  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1439  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  1440  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  1441  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  1442  			{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  1443  		},
  1444  	)
  1445  	defer restore()
  1446  
  1447  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
  1448  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
  1449  	snapdMnt := filepath.Join(boot.InitramfsRunMntDir, "snapd")
  1450  
  1451  	// don't do anything from systemd-mount, we verify the arguments passed at
  1452  	// the end with cmd.Calls
  1453  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
  1454  	defer cmd.Restore()
  1455  
  1456  	isMountedChecks := []string{}
  1457  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
  1458  		isMountedChecks = append(isMountedChecks, where)
  1459  		return true, nil
  1460  	})
  1461  	defer restore()
  1462  
  1463  	// mock a bootloader
  1464  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1465  	bootloader.Force(bloader)
  1466  	defer bootloader.Force(nil)
  1467  
  1468  	// set the current kernel
  1469  	restore = bloader.SetEnabledKernel(s.kernel)
  1470  	defer restore()
  1471  
  1472  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1473  
  1474  	// write modeenv
  1475  	modeEnv := boot.Modeenv{
  1476  		Mode:           "run",
  1477  		Base:           s.core20.Filename(),
  1478  		CurrentKernels: []string{s.kernel.Filename()},
  1479  	}
  1480  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1481  	c.Assert(err, IsNil)
  1482  
  1483  	s.testRecoverModeHappy(c)
  1484  
  1485  	c.Check(s.Stdout.String(), Equals, "")
  1486  
  1487  	// check that all of the override files are present
  1488  	for _, initrdUnit := range []string{
  1489  		"initrd.target",
  1490  		"initrd-fs.target",
  1491  		"initrd-switch-root.target",
  1492  		"local-fs.target",
  1493  	} {
  1494  
  1495  		mountUnit := systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSaveDir)
  1496  		fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
  1497  		unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
  1498  		c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
  1499  Requires=%[1]s
  1500  After=%[1]s
  1501  `, mountUnit+".mount"))
  1502  	}
  1503  
  1504  	c.Check(isMountedChecks, DeepEquals, []string{
  1505  		boot.InitramfsUbuntuSeedDir,
  1506  		snapdMnt,
  1507  		kernelMnt,
  1508  		baseMnt,
  1509  		boot.InitramfsDataDir,
  1510  		boot.InitramfsUbuntuBootDir,
  1511  		boot.InitramfsHostUbuntuDataDir,
  1512  		boot.InitramfsUbuntuSaveDir,
  1513  	})
  1514  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1515  		{
  1516  			"systemd-mount",
  1517  			"/dev/disk/by-label/ubuntu-seed",
  1518  			boot.InitramfsUbuntuSeedDir,
  1519  			"--no-pager",
  1520  			"--no-ask-password",
  1521  			"--fsck=yes",
  1522  		}, {
  1523  			"systemd-mount",
  1524  			filepath.Join(s.seedDir, "snaps", s.snapd.Filename()),
  1525  			snapdMnt,
  1526  			"--no-pager",
  1527  			"--no-ask-password",
  1528  			"--fsck=no",
  1529  		}, {
  1530  			"systemd-mount",
  1531  			filepath.Join(s.seedDir, "snaps", s.kernel.Filename()),
  1532  			kernelMnt,
  1533  			"--no-pager",
  1534  			"--no-ask-password",
  1535  			"--fsck=no",
  1536  		}, {
  1537  			"systemd-mount",
  1538  			filepath.Join(s.seedDir, "snaps", s.core20.Filename()),
  1539  			baseMnt,
  1540  			"--no-pager",
  1541  			"--no-ask-password",
  1542  			"--fsck=no",
  1543  		}, {
  1544  			"systemd-mount",
  1545  			"tmpfs",
  1546  			boot.InitramfsDataDir,
  1547  			"--no-pager",
  1548  			"--no-ask-password",
  1549  			"--type=tmpfs",
  1550  			"--fsck=no",
  1551  			"--options=nosuid",
  1552  		}, {
  1553  			"systemd-mount",
  1554  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  1555  			boot.InitramfsUbuntuBootDir,
  1556  			"--no-pager",
  1557  			"--no-ask-password",
  1558  			"--fsck=yes",
  1559  		}, {
  1560  			"systemd-mount",
  1561  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1562  			boot.InitramfsHostUbuntuDataDir,
  1563  			"--no-pager",
  1564  			"--no-ask-password",
  1565  			"--fsck=no",
  1566  			"--options=nosuid",
  1567  		}, {
  1568  			"systemd-mount",
  1569  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  1570  			boot.InitramfsUbuntuSaveDir,
  1571  			"--no-pager",
  1572  			"--no-ask-password",
  1573  			"--fsck=no",
  1574  		},
  1575  	})
  1576  
  1577  	// we should not have written a degraded.json
  1578  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  1579  
  1580  	// save is optional and found in this test
  1581  	c.Check(s.logs.String(), Not(testutil.Contains), "ubuntu-save was not found")
  1582  }
  1583  
  1584  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeHappyNoSaveRealSystemdMount(c *C) {
  1585  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1586  
  1587  	restore := disks.MockMountPointDisksToPartitionMapping(
  1588  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1589  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
  1590  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
  1591  		},
  1592  	)
  1593  	defer restore()
  1594  
  1595  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
  1596  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
  1597  
  1598  	// don't do anything from systemd-mount, we verify the arguments passed at
  1599  	// the end with cmd.Calls
  1600  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
  1601  	defer cmd.Restore()
  1602  
  1603  	// mock that in turn, /run/mnt/ubuntu-boot, /run/mnt/ubuntu-seed, etc. are
  1604  	// mounted
  1605  	n := 0
  1606  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
  1607  		n++
  1608  		switch n {
  1609  		// first call for each mount returns false, then returns true, this
  1610  		// tests in the case where systemd is racy / inconsistent and things
  1611  		// aren't mounted by the time systemd-mount returns
  1612  		case 1, 2:
  1613  			c.Assert(where, Equals, boot.InitramfsUbuntuBootDir)
  1614  			return n%2 == 0, nil
  1615  		case 3, 4:
  1616  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
  1617  			return n%2 == 0, nil
  1618  		case 5, 6:
  1619  			c.Assert(where, Equals, boot.InitramfsDataDir)
  1620  			return n%2 == 0, nil
  1621  		case 7, 8:
  1622  			c.Assert(where, Equals, baseMnt)
  1623  			return n%2 == 0, nil
  1624  		case 9, 10:
  1625  			c.Assert(where, Equals, kernelMnt)
  1626  			return n%2 == 0, nil
  1627  		default:
  1628  			c.Errorf("unexpected IsMounted check on %s", where)
  1629  			return false, fmt.Errorf("unexpected IsMounted check on %s", where)
  1630  		}
  1631  	})
  1632  	defer restore()
  1633  
  1634  	// mock a bootloader
  1635  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1636  	bootloader.Force(bloader)
  1637  	defer bootloader.Force(nil)
  1638  
  1639  	// set the current kernel
  1640  	restore = bloader.SetEnabledKernel(s.kernel)
  1641  	defer restore()
  1642  
  1643  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1644  
  1645  	// write modeenv
  1646  	modeEnv := boot.Modeenv{
  1647  		Mode:           "run",
  1648  		Base:           s.core20.Filename(),
  1649  		CurrentKernels: []string{s.kernel.Filename()},
  1650  	}
  1651  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1652  	c.Assert(err, IsNil)
  1653  
  1654  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1655  	c.Assert(err, IsNil)
  1656  	c.Check(s.Stdout.String(), Equals, "")
  1657  
  1658  	// check that all of the override files are present
  1659  	for _, initrdUnit := range []string{
  1660  		"initrd.target",
  1661  		"initrd-fs.target",
  1662  		"initrd-switch-root.target",
  1663  		"local-fs.target",
  1664  	} {
  1665  		for _, mountUnit := range []string{
  1666  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuBootDir),
  1667  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSeedDir),
  1668  			systemd.EscapeUnitNamePath(boot.InitramfsDataDir),
  1669  			systemd.EscapeUnitNamePath(baseMnt),
  1670  			systemd.EscapeUnitNamePath(kernelMnt),
  1671  		} {
  1672  			fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
  1673  			unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
  1674  			c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
  1675  Requires=%[1]s
  1676  After=%[1]s
  1677  `, mountUnit+".mount"))
  1678  		}
  1679  	}
  1680  
  1681  	// 2 IsMounted calls per mount point, so 10 total IsMounted calls
  1682  	c.Assert(n, Equals, 10)
  1683  
  1684  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
  1685  		{
  1686  			"systemd-mount",
  1687  			"/dev/disk/by-label/ubuntu-boot",
  1688  			boot.InitramfsUbuntuBootDir,
  1689  			"--no-pager",
  1690  			"--no-ask-password",
  1691  			"--fsck=yes",
  1692  		}, {
  1693  			"systemd-mount",
  1694  			"/dev/disk/by-partuuid/ubuntu-seed-partuuid",
  1695  			boot.InitramfsUbuntuSeedDir,
  1696  			"--no-pager",
  1697  			"--no-ask-password",
  1698  			"--fsck=yes",
  1699  		}, {
  1700  			"systemd-mount",
  1701  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1702  			boot.InitramfsDataDir,
  1703  			"--no-pager",
  1704  			"--no-ask-password",
  1705  			"--fsck=yes",
  1706  			"--options=nosuid",
  1707  		}, {
  1708  			"systemd-mount",
  1709  			filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), s.core20.Filename()),
  1710  			baseMnt,
  1711  			"--no-pager",
  1712  			"--no-ask-password",
  1713  			"--fsck=no",
  1714  		}, {
  1715  			"systemd-mount",
  1716  			filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), s.kernel.Filename()),
  1717  			kernelMnt,
  1718  			"--no-pager",
  1719  			"--no-ask-password",
  1720  			"--fsck=no",
  1721  		},
  1722  	})
  1723  }
  1724  
  1725  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeWithSaveHappyRealSystemdMount(c *C) {
  1726  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1727  
  1728  	restore := disks.MockMountPointDisksToPartitionMapping(
  1729  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1730  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootWithSaveDisk,
  1731  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootWithSaveDisk,
  1732  			{Mountpoint: boot.InitramfsUbuntuSaveDir}: defaultBootWithSaveDisk,
  1733  		},
  1734  	)
  1735  	defer restore()
  1736  
  1737  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
  1738  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
  1739  
  1740  	// don't do anything from systemd-mount, we verify the arguments passed at
  1741  	// the end with cmd.Calls
  1742  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
  1743  	defer cmd.Restore()
  1744  
  1745  	isMountedChecks := []string{}
  1746  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
  1747  		isMountedChecks = append(isMountedChecks, where)
  1748  		return true, nil
  1749  	})
  1750  	defer restore()
  1751  
  1752  	// mock a bootloader
  1753  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1754  	bootloader.Force(bloader)
  1755  	defer bootloader.Force(nil)
  1756  
  1757  	// set the current kernel
  1758  	restore = bloader.SetEnabledKernel(s.kernel)
  1759  	defer restore()
  1760  
  1761  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1762  
  1763  	// write modeenv
  1764  	modeEnv := boot.Modeenv{
  1765  		Mode:           "run",
  1766  		Base:           s.core20.Filename(),
  1767  		CurrentKernels: []string{s.kernel.Filename()},
  1768  	}
  1769  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1770  	c.Assert(err, IsNil)
  1771  
  1772  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1773  	c.Assert(err, IsNil)
  1774  	c.Check(s.Stdout.String(), Equals, "")
  1775  
  1776  	// check that all of the override files are present
  1777  	for _, initrdUnit := range []string{
  1778  		"initrd.target",
  1779  		"initrd-fs.target",
  1780  		"initrd-switch-root.target",
  1781  		"local-fs.target",
  1782  	} {
  1783  
  1784  		mountUnit := systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSaveDir)
  1785  		fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
  1786  		unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
  1787  		c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
  1788  Requires=%[1]s
  1789  After=%[1]s
  1790  `, mountUnit+".mount"))
  1791  	}
  1792  
  1793  	c.Check(isMountedChecks, DeepEquals, []string{
  1794  		boot.InitramfsUbuntuBootDir,
  1795  		boot.InitramfsUbuntuSeedDir,
  1796  		boot.InitramfsDataDir,
  1797  		boot.InitramfsUbuntuSaveDir,
  1798  		baseMnt,
  1799  		kernelMnt,
  1800  	})
  1801  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1802  		{
  1803  			"systemd-mount",
  1804  			"/dev/disk/by-label/ubuntu-boot",
  1805  			boot.InitramfsUbuntuBootDir,
  1806  			"--no-pager",
  1807  			"--no-ask-password",
  1808  			"--fsck=yes",
  1809  		}, {
  1810  			"systemd-mount",
  1811  			"/dev/disk/by-partuuid/ubuntu-seed-partuuid",
  1812  			boot.InitramfsUbuntuSeedDir,
  1813  			"--no-pager",
  1814  			"--no-ask-password",
  1815  			"--fsck=yes",
  1816  		}, {
  1817  			"systemd-mount",
  1818  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1819  			boot.InitramfsDataDir,
  1820  			"--no-pager",
  1821  			"--no-ask-password",
  1822  			"--fsck=yes",
  1823  			"--options=nosuid",
  1824  		}, {
  1825  			"systemd-mount",
  1826  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  1827  			boot.InitramfsUbuntuSaveDir,
  1828  			"--no-pager",
  1829  			"--no-ask-password",
  1830  			"--fsck=yes",
  1831  		}, {
  1832  			"systemd-mount",
  1833  			filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), s.core20.Filename()),
  1834  			baseMnt,
  1835  			"--no-pager",
  1836  			"--no-ask-password",
  1837  			"--fsck=no",
  1838  		}, {
  1839  			"systemd-mount",
  1840  			filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), s.kernel.Filename()),
  1841  			kernelMnt,
  1842  			"--no-pager",
  1843  			"--no-ask-password",
  1844  			"--fsck=no",
  1845  		},
  1846  	})
  1847  }
  1848  
  1849  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeFirstBootRecoverySystemSetHappy(c *C) {
  1850  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1851  
  1852  	restore := disks.MockMountPointDisksToPartitionMapping(
  1853  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1854  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootWithSaveDisk,
  1855  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootWithSaveDisk,
  1856  			{Mountpoint: boot.InitramfsUbuntuSaveDir}: defaultBootWithSaveDisk,
  1857  		},
  1858  	)
  1859  	defer restore()
  1860  
  1861  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1862  		ubuntuLabelMount("ubuntu-boot", "run"),
  1863  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1864  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
  1865  		ubuntuPartUUIDMount("ubuntu-save-partuuid", "run"),
  1866  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1867  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1868  		// RecoverySystem set makes us mount the snapd snap here
  1869  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  1870  	}, nil)
  1871  	defer restore()
  1872  
  1873  	// mock a bootloader
  1874  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1875  	bootloader.Force(bloader)
  1876  	defer bootloader.Force(nil)
  1877  
  1878  	// set the current kernel
  1879  	restore = bloader.SetEnabledKernel(s.kernel)
  1880  	defer restore()
  1881  
  1882  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1883  
  1884  	// write modeenv
  1885  	modeEnv := boot.Modeenv{
  1886  		Mode:           "run",
  1887  		RecoverySystem: "20191118",
  1888  		Base:           s.core20.Filename(),
  1889  		CurrentKernels: []string{s.kernel.Filename()},
  1890  	}
  1891  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1892  	c.Assert(err, IsNil)
  1893  
  1894  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1895  	c.Assert(err, IsNil)
  1896  
  1897  	// we should not have written a degraded.json
  1898  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  1899  }
  1900  
  1901  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeWithBootedKernelPartUUIDHappy(c *C) {
  1902  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1903  
  1904  	restore := main.MockPartitionUUIDForBootedKernelDisk("ubuntu-boot-partuuid")
  1905  	defer restore()
  1906  
  1907  	restore = disks.MockMountPointDisksToPartitionMapping(
  1908  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1909  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
  1910  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
  1911  		},
  1912  	)
  1913  	defer restore()
  1914  
  1915  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1916  		{
  1917  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  1918  			boot.InitramfsUbuntuBootDir,
  1919  			needsFsckDiskMountOpts,
  1920  		},
  1921  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1922  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
  1923  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1924  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1925  	}, nil)
  1926  	defer restore()
  1927  
  1928  	// mock a bootloader
  1929  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1930  	bootloader.Force(bloader)
  1931  	defer bootloader.Force(nil)
  1932  
  1933  	// set the current kernel
  1934  	restore = bloader.SetEnabledKernel(s.kernel)
  1935  	defer restore()
  1936  
  1937  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1938  
  1939  	// write modeenv
  1940  	modeEnv := boot.Modeenv{
  1941  		Mode:           "run",
  1942  		Base:           s.core20.Filename(),
  1943  		CurrentKernels: []string{s.kernel.Filename()},
  1944  	}
  1945  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1946  	c.Assert(err, IsNil)
  1947  
  1948  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1949  	c.Assert(err, IsNil)
  1950  }
  1951  
  1952  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedDataHappy(c *C) {
  1953  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1954  
  1955  	// ensure that we check that access to sealed keys were locked
  1956  	sealedKeysLocked := false
  1957  	defer main.MockSecbootLockSealedKeys(func() error {
  1958  		sealedKeysLocked = true
  1959  		return nil
  1960  	})()
  1961  
  1962  	restore := disks.MockMountPointDisksToPartitionMapping(
  1963  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1964  			{Mountpoint: boot.InitramfsUbuntuBootDir}:                          defaultEncBootDisk,
  1965  			{Mountpoint: boot.InitramfsDataDir, IsDecryptedDevice: true}:       defaultEncBootDisk,
  1966  			{Mountpoint: boot.InitramfsUbuntuSaveDir, IsDecryptedDevice: true}: defaultEncBootDisk,
  1967  		},
  1968  	)
  1969  	defer restore()
  1970  
  1971  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1972  		ubuntuLabelMount("ubuntu-boot", "run"),
  1973  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1974  		{
  1975  			"/dev/mapper/ubuntu-data-random",
  1976  			boot.InitramfsDataDir,
  1977  			needsFsckAndNoSuidDiskMountOpts,
  1978  		},
  1979  		{
  1980  			"/dev/mapper/ubuntu-save-random",
  1981  			boot.InitramfsUbuntuSaveDir,
  1982  			needsFsckDiskMountOpts,
  1983  		},
  1984  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1985  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1986  	}, nil)
  1987  	defer restore()
  1988  
  1989  	// write the installed model like makebootable does it
  1990  	err := os.MkdirAll(filepath.Join(boot.InitramfsUbuntuBootDir, "device"), 0755)
  1991  	c.Assert(err, IsNil)
  1992  	mf, err := os.Create(filepath.Join(boot.InitramfsUbuntuBootDir, "device/model"))
  1993  	c.Assert(err, IsNil)
  1994  	defer mf.Close()
  1995  	err = asserts.NewEncoder(mf).Encode(s.model)
  1996  	c.Assert(err, IsNil)
  1997  
  1998  	dataActivated := false
  1999  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  2000  		c.Assert(name, Equals, "ubuntu-data")
  2001  		c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  2002  		c.Assert(opts.AllowRecoveryKey, Equals, true)
  2003  		c.Assert(opts.WhichModel, NotNil)
  2004  		mod, err := opts.WhichModel()
  2005  		c.Assert(err, IsNil)
  2006  		c.Check(mod.Model(), Equals, "my-model")
  2007  
  2008  		dataActivated = true
  2009  		// return true because we are using an encrypted device
  2010  		return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  2011  	})
  2012  	defer restore()
  2013  
  2014  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsWritableDir, "foo", "marker")
  2015  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  2016  
  2017  	saveActivated := false
  2018  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  2019  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  2020  		saveActivated = true
  2021  		c.Assert(name, Equals, "ubuntu-save")
  2022  		c.Assert(key, DeepEquals, []byte("foo"))
  2023  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  2024  	})
  2025  	defer restore()
  2026  
  2027  	measureEpochCalls := 0
  2028  	measureModelCalls := 0
  2029  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  2030  		measureEpochCalls++
  2031  		return nil
  2032  	})
  2033  	defer restore()
  2034  
  2035  	var measuredModel *asserts.Model
  2036  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  2037  		measureModelCalls++
  2038  		var err error
  2039  		measuredModel, err = findModel()
  2040  		if err != nil {
  2041  			return err
  2042  		}
  2043  		return nil
  2044  	})
  2045  	defer restore()
  2046  
  2047  	// mock a bootloader
  2048  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2049  	bootloader.Force(bloader)
  2050  	defer bootloader.Force(nil)
  2051  
  2052  	// set the current kernel
  2053  	restore = bloader.SetEnabledKernel(s.kernel)
  2054  	defer restore()
  2055  
  2056  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  2057  
  2058  	// write modeenv
  2059  	modeEnv := boot.Modeenv{
  2060  		Mode:           "run",
  2061  		Base:           s.core20.Filename(),
  2062  		CurrentKernels: []string{s.kernel.Filename()},
  2063  	}
  2064  	err = modeEnv.WriteTo(boot.InitramfsWritableDir)
  2065  	c.Assert(err, IsNil)
  2066  
  2067  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  2068  	c.Assert(err, IsNil)
  2069  	c.Check(dataActivated, Equals, true)
  2070  	c.Check(saveActivated, Equals, true)
  2071  	c.Check(measureEpochCalls, Equals, 1)
  2072  	c.Check(measureModelCalls, Equals, 1)
  2073  	c.Check(measuredModel, DeepEquals, s.model)
  2074  	c.Check(sealedKeysLocked, Equals, true)
  2075  
  2076  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  2077  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "run-model-measured"), testutil.FilePresent)
  2078  }
  2079  
  2080  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedDataUnhappyNoSave(c *C) {
  2081  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  2082  
  2083  	defaultEncNoSaveBootDisk := &disks.MockDiskMapping{
  2084  		FilesystemLabelToPartUUID: map[string]string{
  2085  			"ubuntu-boot":     "ubuntu-boot-partuuid",
  2086  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  2087  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
  2088  			// missing ubuntu-save
  2089  		},
  2090  		DiskHasPartitions: true,
  2091  		DevNum:            "defaultEncDev",
  2092  	}
  2093  
  2094  	restore := disks.MockMountPointDisksToPartitionMapping(
  2095  		map[disks.Mountpoint]*disks.MockDiskMapping{
  2096  			{Mountpoint: boot.InitramfsUbuntuBootDir}:                    defaultEncNoSaveBootDisk,
  2097  			{Mountpoint: boot.InitramfsDataDir, IsDecryptedDevice: true}: defaultEncNoSaveBootDisk,
  2098  		},
  2099  	)
  2100  	defer restore()
  2101  
  2102  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  2103  		ubuntuLabelMount("ubuntu-boot", "run"),
  2104  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  2105  		{
  2106  			"/dev/mapper/ubuntu-data-random",
  2107  			boot.InitramfsDataDir,
  2108  			needsFsckAndNoSuidDiskMountOpts,
  2109  		},
  2110  	}, nil)
  2111  	defer restore()
  2112  
  2113  	dataActivated := false
  2114  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  2115  		c.Assert(name, Equals, "ubuntu-data")
  2116  		dataActivated = true
  2117  		// return true because we are using an encrypted device
  2118  		return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  2119  	})
  2120  	defer restore()
  2121  
  2122  	// the test does not mock ubuntu-save.key, the secboot helper for
  2123  	// opening a volume using the key should not be called
  2124  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  2125  		c.Fatal("unexpected call")
  2126  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  2127  	})
  2128  	defer restore()
  2129  
  2130  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error { return nil })
  2131  	defer restore()
  2132  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  2133  		return nil
  2134  	})
  2135  	defer restore()
  2136  
  2137  	// mock a bootloader
  2138  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2139  	bootloader.Force(bloader)
  2140  	defer bootloader.Force(nil)
  2141  
  2142  	// set the current kernel
  2143  	restore = bloader.SetEnabledKernel(s.kernel)
  2144  	defer restore()
  2145  
  2146  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  2147  
  2148  	// write modeenv
  2149  	modeEnv := boot.Modeenv{
  2150  		Mode:           "run",
  2151  		Base:           s.core20.Filename(),
  2152  		CurrentKernels: []string{s.kernel.Filename()},
  2153  	}
  2154  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  2155  	c.Assert(err, IsNil)
  2156  
  2157  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  2158  	c.Assert(err, ErrorMatches, "cannot find ubuntu-save encryption key at .*/run/mnt/data/system-data/var/lib/snapd/device/fde/ubuntu-save.key")
  2159  	c.Check(dataActivated, Equals, true)
  2160  }
  2161  
  2162  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedDataUnhappyUnlockSaveFail(c *C) {
  2163  	// ensure that we check that access to sealed keys were locked
  2164  	sealedKeysLocked := false
  2165  	defer main.MockSecbootLockSealedKeys(func() error {
  2166  		sealedKeysLocked = true
  2167  		return fmt.Errorf("blocking keys failed")
  2168  	})()
  2169  
  2170  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  2171  	restore := disks.MockMountPointDisksToPartitionMapping(
  2172  		map[disks.Mountpoint]*disks.MockDiskMapping{
  2173  			{Mountpoint: boot.InitramfsUbuntuBootDir}:                          defaultEncBootDisk,
  2174  			{Mountpoint: boot.InitramfsDataDir, IsDecryptedDevice: true}:       defaultEncBootDisk,
  2175  			{Mountpoint: boot.InitramfsUbuntuSaveDir, IsDecryptedDevice: true}: defaultEncBootDisk,
  2176  		},
  2177  	)
  2178  	defer restore()
  2179  
  2180  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  2181  		ubuntuLabelMount("ubuntu-boot", "run"),
  2182  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  2183  		{
  2184  			"/dev/mapper/ubuntu-data-random",
  2185  			boot.InitramfsDataDir,
  2186  			needsFsckAndNoSuidDiskMountOpts,
  2187  		},
  2188  	}, nil)
  2189  	defer restore()
  2190  
  2191  	dataActivated := false
  2192  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  2193  		c.Assert(name, Equals, "ubuntu-data")
  2194  		dataActivated = true
  2195  		// return true because we are using an encrypted device
  2196  		return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  2197  	})
  2198  	defer restore()
  2199  
  2200  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsWritableDir, "foo", "")
  2201  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  2202  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not yet activated"))
  2203  		return foundEncrypted("ubuntu-save"), fmt.Errorf("ubuntu-save unlock fail")
  2204  	})
  2205  	defer restore()
  2206  
  2207  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error { return nil })
  2208  	defer restore()
  2209  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  2210  		return nil
  2211  	})
  2212  	defer restore()
  2213  
  2214  	// mock a bootloader
  2215  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2216  	bootloader.Force(bloader)
  2217  	defer bootloader.Force(nil)
  2218  
  2219  	// set the current kernel
  2220  	restore = bloader.SetEnabledKernel(s.kernel)
  2221  	defer restore()
  2222  
  2223  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  2224  
  2225  	// write modeenv
  2226  	modeEnv := boot.Modeenv{
  2227  		Mode:           "run",
  2228  		Base:           s.core20.Filename(),
  2229  		CurrentKernels: []string{s.kernel.Filename()},
  2230  	}
  2231  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  2232  	c.Assert(err, IsNil)
  2233  
  2234  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  2235  	c.Assert(err, ErrorMatches, "cannot unlock ubuntu-save volume: ubuntu-save unlock fail")
  2236  	c.Check(dataActivated, Equals, true)
  2237  	// locking sealing keys was attempted, error was only logged
  2238  	c.Check(sealedKeysLocked, Equals, true)
  2239  }
  2240  
  2241  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedNoModel(c *C) {
  2242  	s.testInitramfsMountsEncryptedNoModel(c, "run", "", 1)
  2243  }
  2244  
  2245  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeEncryptedNoModel(c *C) {
  2246  	s.testInitramfsMountsEncryptedNoModel(c, "install", s.sysLabel, 0)
  2247  }
  2248  
  2249  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedNoModel(c *C) {
  2250  	s.testInitramfsMountsEncryptedNoModel(c, "recover", s.sysLabel, 0)
  2251  }
  2252  
  2253  func (s *initramfsMountsSuite) testInitramfsMountsEncryptedNoModel(c *C, mode, label string, expectedMeasureModelCalls int) {
  2254  	s.mockProcCmdlineContent(c, fmt.Sprintf("snapd_recovery_mode=%s", mode))
  2255  
  2256  	// ensure that we check that access to sealed keys were locked
  2257  	sealedKeysLocked := false
  2258  	defer main.MockSecbootLockSealedKeys(func() error {
  2259  		sealedKeysLocked = true
  2260  		return fmt.Errorf("blocking keys failed")
  2261  	})()
  2262  
  2263  	// install and recover mounts are just ubuntu-seed before we fail
  2264  	var restore func()
  2265  	if mode == "run" {
  2266  		// run mode will mount ubuntu-boot and ubuntu-seed
  2267  		restore = s.mockSystemdMountSequence(c, []systemdMount{
  2268  			ubuntuLabelMount("ubuntu-boot", mode),
  2269  			ubuntuPartUUIDMount("ubuntu-seed-partuuid", mode),
  2270  		}, nil)
  2271  		restore2 := disks.MockMountPointDisksToPartitionMapping(
  2272  			map[disks.Mountpoint]*disks.MockDiskMapping{
  2273  				{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  2274  			},
  2275  		)
  2276  		defer restore2()
  2277  	} else {
  2278  		restore = s.mockSystemdMountSequence(c, []systemdMount{
  2279  			ubuntuLabelMount("ubuntu-seed", mode),
  2280  		}, nil)
  2281  
  2282  		// in install / recover mode the code doesn't make it far enough to do
  2283  		// any disk cross checking
  2284  	}
  2285  	defer restore()
  2286  
  2287  	if label != "" {
  2288  		s.mockProcCmdlineContent(c,
  2289  			fmt.Sprintf("snapd_recovery_mode=%s snapd_recovery_system=%s", mode, label))
  2290  		// break the seed
  2291  		err := os.Remove(filepath.Join(s.seedDir, "systems", label, "model"))
  2292  		c.Assert(err, IsNil)
  2293  	}
  2294  
  2295  	measureEpochCalls := 0
  2296  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  2297  		measureEpochCalls++
  2298  		return nil
  2299  	})
  2300  	defer restore()
  2301  
  2302  	measureModelCalls := 0
  2303  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  2304  		measureModelCalls++
  2305  		_, err := findModel()
  2306  		if err != nil {
  2307  			return err
  2308  		}
  2309  		return fmt.Errorf("unexpected call")
  2310  	})
  2311  	defer restore()
  2312  
  2313  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  2314  	where := "/run/mnt/ubuntu-boot/device/model"
  2315  	if mode != "run" {
  2316  		where = fmt.Sprintf("/run/mnt/ubuntu-seed/systems/%s/model", label)
  2317  	}
  2318  	c.Assert(err, ErrorMatches, fmt.Sprintf(".*cannot read model assertion: open .*%s: no such file or directory", where))
  2319  	c.Assert(measureEpochCalls, Equals, 1)
  2320  	c.Assert(measureModelCalls, Equals, expectedMeasureModelCalls)
  2321  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  2322  	gl, err := filepath.Glob(filepath.Join(dirs.SnapBootstrapRunDir, "*-model-measured"))
  2323  	c.Assert(err, IsNil)
  2324  	c.Assert(gl, HasLen, 0)
  2325  	c.Check(sealedKeysLocked, Equals, true)
  2326  }
  2327  
  2328  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeUpgradeScenarios(c *C) {
  2329  	tt := []struct {
  2330  		modeenv *boot.Modeenv
  2331  		// this is a function so we can have delayed execution, typical values
  2332  		// depend on the root dir which changes for each test case
  2333  		additionalMountsFunc func() []systemdMount
  2334  		enableKernel         snap.PlaceInfo
  2335  		enableTryKernel      snap.PlaceInfo
  2336  		snapFiles            []snap.PlaceInfo
  2337  		kernelStatus         string
  2338  
  2339  		expRebootPanic string
  2340  		expLog         string
  2341  		expError       string
  2342  		expModeenv     *boot.Modeenv
  2343  		comment        string
  2344  	}{
  2345  		// default case no upgrades
  2346  		{
  2347  			modeenv: &boot.Modeenv{
  2348  				Mode:           "run",
  2349  				Base:           s.core20.Filename(),
  2350  				CurrentKernels: []string{s.kernel.Filename()},
  2351  			},
  2352  			additionalMountsFunc: func() []systemdMount {
  2353  				return []systemdMount{
  2354  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  2355  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  2356  				}
  2357  			},
  2358  			enableKernel: s.kernel,
  2359  			snapFiles:    []snap.PlaceInfo{s.core20, s.kernel},
  2360  			comment:      "happy default no upgrades",
  2361  		},
  2362  
  2363  		// happy upgrade cases
  2364  		{
  2365  			modeenv: &boot.Modeenv{
  2366  				Mode:           "run",
  2367  				Base:           s.core20.Filename(),
  2368  				CurrentKernels: []string{s.kernel.Filename(), s.kernelr2.Filename()},
  2369  			},
  2370  			additionalMountsFunc: func() []systemdMount {
  2371  				return []systemdMount{
  2372  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  2373  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernelr2),
  2374  				}
  2375  			},
  2376  			kernelStatus:    boot.TryingStatus,
  2377  			enableKernel:    s.kernel,
  2378  			enableTryKernel: s.kernelr2,
  2379  			snapFiles:       []snap.PlaceInfo{s.core20, s.kernel, s.kernelr2},
  2380  			comment:         "happy kernel snap upgrade",
  2381  		},
  2382  		{
  2383  			modeenv: &boot.Modeenv{
  2384  				Mode:           "run",
  2385  				Base:           s.core20.Filename(),
  2386  				TryBase:        s.core20r2.Filename(),
  2387  				BaseStatus:     boot.TryStatus,
  2388  				CurrentKernels: []string{s.kernel.Filename()},
  2389  			},
  2390  			additionalMountsFunc: func() []systemdMount {
  2391  				return []systemdMount{
  2392  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20r2),
  2393  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  2394  				}
  2395  			},
  2396  			enableKernel: s.kernel,
  2397  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20, s.core20r2},
  2398  			expModeenv: &boot.Modeenv{
  2399  				Mode:           "run",
  2400  				Base:           s.core20.Filename(),
  2401  				TryBase:        s.core20r2.Filename(),
  2402  				BaseStatus:     boot.TryingStatus,
  2403  				CurrentKernels: []string{s.kernel.Filename()},
  2404  			},
  2405  			comment: "happy base snap upgrade",
  2406  		},
  2407  		{
  2408  			modeenv: &boot.Modeenv{
  2409  				Mode:           "run",
  2410  				Base:           s.core20.Filename(),
  2411  				TryBase:        s.core20r2.Filename(),
  2412  				BaseStatus:     boot.TryStatus,
  2413  				CurrentKernels: []string{s.kernel.Filename(), s.kernelr2.Filename()},
  2414  			},
  2415  			additionalMountsFunc: func() []systemdMount {
  2416  				return []systemdMount{
  2417  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20r2),
  2418  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernelr2),
  2419  				}
  2420  			},
  2421  			enableKernel:    s.kernel,
  2422  			enableTryKernel: s.kernelr2,
  2423  			snapFiles:       []snap.PlaceInfo{s.kernel, s.kernelr2, s.core20, s.core20r2},
  2424  			kernelStatus:    boot.TryingStatus,
  2425  			expModeenv: &boot.Modeenv{
  2426  				Mode:           "run",
  2427  				Base:           s.core20.Filename(),
  2428  				TryBase:        s.core20r2.Filename(),
  2429  				BaseStatus:     boot.TryingStatus,
  2430  				CurrentKernels: []string{s.kernel.Filename(), s.kernelr2.Filename()},
  2431  			},
  2432  			comment: "happy simultaneous base snap and kernel snap upgrade",
  2433  		},
  2434  
  2435  		// fallback cases
  2436  		{
  2437  			modeenv: &boot.Modeenv{
  2438  				Mode:           "run",
  2439  				Base:           s.core20.Filename(),
  2440  				TryBase:        s.core20r2.Filename(),
  2441  				BaseStatus:     boot.TryStatus,
  2442  				CurrentKernels: []string{s.kernel.Filename()},
  2443  			},
  2444  			additionalMountsFunc: func() []systemdMount {
  2445  				return []systemdMount{
  2446  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  2447  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  2448  				}
  2449  			},
  2450  			enableKernel: s.kernel,
  2451  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20},
  2452  			comment:      "happy fallback try base not existing",
  2453  		},
  2454  		{
  2455  			modeenv: &boot.Modeenv{
  2456  				Mode:           "run",
  2457  				Base:           s.core20.Filename(),
  2458  				BaseStatus:     boot.TryStatus,
  2459  				TryBase:        "",
  2460  				CurrentKernels: []string{s.kernel.Filename()},
  2461  			},
  2462  			additionalMountsFunc: func() []systemdMount {
  2463  				return []systemdMount{
  2464  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  2465  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  2466  				}
  2467  			},
  2468  			enableKernel: s.kernel,
  2469  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20},
  2470  			comment:      "happy fallback base_status try, empty try_base",
  2471  		},
  2472  		{
  2473  			modeenv: &boot.Modeenv{
  2474  				Mode:           "run",
  2475  				Base:           s.core20.Filename(),
  2476  				TryBase:        s.core20r2.Filename(),
  2477  				BaseStatus:     boot.TryingStatus,
  2478  				CurrentKernels: []string{s.kernel.Filename()},
  2479  			},
  2480  			additionalMountsFunc: func() []systemdMount {
  2481  				return []systemdMount{
  2482  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  2483  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  2484  				}
  2485  			},
  2486  			enableKernel: s.kernel,
  2487  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20, s.core20r2},
  2488  			expModeenv: &boot.Modeenv{
  2489  				Mode:           "run",
  2490  				Base:           s.core20.Filename(),
  2491  				TryBase:        s.core20r2.Filename(),
  2492  				BaseStatus:     boot.DefaultStatus,
  2493  				CurrentKernels: []string{s.kernel.Filename()},
  2494  			},
  2495  			comment: "happy fallback failed boot with try snap",
  2496  		},
  2497  		{
  2498  			modeenv: &boot.Modeenv{
  2499  				Mode:           "run",
  2500  				Base:           s.core20.Filename(),
  2501  				CurrentKernels: []string{s.kernel.Filename()},
  2502  			},
  2503  			enableKernel:    s.kernel,
  2504  			enableTryKernel: s.kernelr2,
  2505  			snapFiles:       []snap.PlaceInfo{s.core20, s.kernel, s.kernelr2},
  2506  			kernelStatus:    boot.TryingStatus,
  2507  			expRebootPanic:  "reboot due to untrusted try kernel snap",
  2508  			comment:         "happy fallback untrusted try kernel snap",
  2509  		},
  2510  		// TODO:UC20: if we ever have a way to compare what kernel was booted,
  2511  		//            and we compute that the booted kernel was the try kernel,
  2512  		//            but the try kernel is not enabled on the bootloader
  2513  		//            (somehow??), then this should become a reboot case rather
  2514  		//            than mount the old kernel snap
  2515  		{
  2516  			modeenv: &boot.Modeenv{
  2517  				Mode:           "run",
  2518  				Base:           s.core20.Filename(),
  2519  				CurrentKernels: []string{s.kernel.Filename()},
  2520  			},
  2521  			kernelStatus:   boot.TryingStatus,
  2522  			enableKernel:   s.kernel,
  2523  			snapFiles:      []snap.PlaceInfo{s.core20, s.kernel},
  2524  			expRebootPanic: "reboot due to no try kernel snap",
  2525  			comment:        "happy fallback kernel_status trying no try kernel",
  2526  		},
  2527  
  2528  		// unhappy cases
  2529  		{
  2530  			modeenv: &boot.Modeenv{
  2531  				Mode: "run",
  2532  			},
  2533  			expError: "fallback base snap unusable: cannot get snap revision: modeenv base boot variable is empty",
  2534  			comment:  "unhappy empty modeenv",
  2535  		},
  2536  		{
  2537  			modeenv: &boot.Modeenv{
  2538  				Mode:           "run",
  2539  				Base:           s.core20.Filename(),
  2540  				CurrentKernels: []string{s.kernel.Filename()},
  2541  			},
  2542  			enableKernel: s.kernelr2,
  2543  			snapFiles:    []snap.PlaceInfo{s.core20, s.kernelr2},
  2544  			expError:     fmt.Sprintf("fallback kernel snap %q is not trusted in the modeenv", s.kernelr2.Filename()),
  2545  			comment:      "unhappy untrusted main kernel snap",
  2546  		},
  2547  	}
  2548  
  2549  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  2550  
  2551  	for _, t := range tt {
  2552  		comment := Commentf(t.comment)
  2553  
  2554  		var cleanups []func()
  2555  
  2556  		if t.expRebootPanic != "" {
  2557  			r := boot.MockInitramfsReboot(func() error {
  2558  				panic(t.expRebootPanic)
  2559  			})
  2560  			cleanups = append(cleanups, r)
  2561  		}
  2562  
  2563  		// setup unique root dir per test
  2564  		rootDir := c.MkDir()
  2565  		cleanups = append(cleanups, func() { dirs.SetRootDir(dirs.GlobalRootDir) })
  2566  		dirs.SetRootDir(rootDir)
  2567  
  2568  		restore := disks.MockMountPointDisksToPartitionMapping(
  2569  			map[disks.Mountpoint]*disks.MockDiskMapping{
  2570  				{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
  2571  				{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
  2572  			},
  2573  		)
  2574  		cleanups = append(cleanups, restore)
  2575  
  2576  		// setup expected systemd-mount calls - every test case has ubuntu-boot,
  2577  		// ubuntu-seed and ubuntu-data mounts because all those mounts happen
  2578  		// before any boot logic
  2579  		mnts := []systemdMount{
  2580  			ubuntuLabelMount("ubuntu-boot", "run"),
  2581  			ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  2582  			ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
  2583  		}
  2584  		if t.additionalMountsFunc != nil {
  2585  			mnts = append(mnts, t.additionalMountsFunc()...)
  2586  		}
  2587  		cleanups = append(cleanups, s.mockSystemdMountSequence(c, mnts, comment))
  2588  
  2589  		// mock a bootloader
  2590  		bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2591  		bootloader.Force(bloader)
  2592  		cleanups = append(cleanups, func() { bootloader.Force(nil) })
  2593  
  2594  		if t.enableKernel != nil {
  2595  			// don't need to restore since each test case has a unique bloader
  2596  			bloader.SetEnabledKernel(t.enableKernel)
  2597  		}
  2598  
  2599  		if t.enableTryKernel != nil {
  2600  			bloader.SetEnabledTryKernel(t.enableTryKernel)
  2601  		}
  2602  
  2603  		// set the kernel_status boot var
  2604  		err := bloader.SetBootVars(map[string]string{"kernel_status": t.kernelStatus})
  2605  		c.Assert(err, IsNil, comment)
  2606  
  2607  		// write the initial modeenv
  2608  		err = t.modeenv.WriteTo(boot.InitramfsWritableDir)
  2609  		c.Assert(err, IsNil, comment)
  2610  
  2611  		// make the snap files - no restore needed because we use a unique root
  2612  		// dir for each test case
  2613  		makeSnapFilesOnEarlyBootUbuntuData(c, t.snapFiles...)
  2614  
  2615  		if t.expRebootPanic != "" {
  2616  			f := func() { main.Parser().ParseArgs([]string{"initramfs-mounts"}) }
  2617  			c.Assert(f, PanicMatches, t.expRebootPanic, comment)
  2618  		} else {
  2619  			_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  2620  			if t.expError != "" {
  2621  				c.Assert(err, ErrorMatches, t.expError, comment)
  2622  			} else {
  2623  				c.Assert(err, IsNil, comment)
  2624  
  2625  				// check the resultant modeenv
  2626  				// if the expModeenv is nil, we just compare to the start
  2627  				newModeenv, err := boot.ReadModeenv(boot.InitramfsWritableDir)
  2628  				c.Assert(err, IsNil, comment)
  2629  				m := t.modeenv
  2630  				if t.expModeenv != nil {
  2631  					m = t.expModeenv
  2632  				}
  2633  				c.Assert(newModeenv.BaseStatus, DeepEquals, m.BaseStatus, comment)
  2634  				c.Assert(newModeenv.TryBase, DeepEquals, m.TryBase, comment)
  2635  				c.Assert(newModeenv.Base, DeepEquals, m.Base, comment)
  2636  			}
  2637  		}
  2638  
  2639  		for _, r := range cleanups {
  2640  			r()
  2641  		}
  2642  	}
  2643  }
  2644  
  2645  func (s *initramfsMountsSuite) testRecoverModeHappy(c *C) {
  2646  	// ensure that we check that access to sealed keys were locked
  2647  	sealedKeysLocked := false
  2648  	restore := main.MockSecbootLockSealedKeys(func() error {
  2649  		sealedKeysLocked = true
  2650  		return nil
  2651  	})
  2652  	defer restore()
  2653  
  2654  	// mock various files that are copied around during recover mode (and files
  2655  	// that shouldn't be copied around)
  2656  	ephemeralUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "data/")
  2657  	err := os.MkdirAll(ephemeralUbuntuData, 0755)
  2658  	c.Assert(err, IsNil)
  2659  	// mock a auth data in the host's ubuntu-data
  2660  	hostUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "host/ubuntu-data/")
  2661  	err = os.MkdirAll(hostUbuntuData, 0755)
  2662  	c.Assert(err, IsNil)
  2663  	mockCopiedFiles := []string{
  2664  		// extrausers
  2665  		"system-data/var/lib/extrausers/passwd",
  2666  		"system-data/var/lib/extrausers/shadow",
  2667  		"system-data/var/lib/extrausers/group",
  2668  		"system-data/var/lib/extrausers/gshadow",
  2669  		// sshd
  2670  		"system-data/etc/ssh/ssh_host_rsa.key",
  2671  		"system-data/etc/ssh/ssh_host_rsa.key.pub",
  2672  		// user ssh
  2673  		"user-data/user1/.ssh/authorized_keys",
  2674  		"user-data/user2/.ssh/authorized_keys",
  2675  		// user snap authentication
  2676  		"user-data/user1/.snap/auth.json",
  2677  		// sudoers
  2678  		"system-data/etc/sudoers.d/create-user-test",
  2679  		// netplan networking
  2680  		"system-data/etc/netplan/00-snapd-config.yaml", // example console-conf filename
  2681  		"system-data/etc/netplan/50-cloud-init.yaml",   // example cloud-init filename
  2682  		// systemd clock file
  2683  		"system-data/var/lib/systemd/timesync/clock",
  2684  		"system-data/etc/machine-id", // machine-id for systemd-networkd
  2685  	}
  2686  	mockUnrelatedFiles := []string{
  2687  		"system-data/var/lib/foo",
  2688  		"system-data/etc/passwd",
  2689  		"user-data/user1/some-random-data",
  2690  		"user-data/user2/other-random-data",
  2691  		"user-data/user2/.snap/sneaky-not-auth.json",
  2692  		"system-data/etc/not-networking/netplan",
  2693  		"system-data/var/lib/systemd/timesync/clock-not-the-clock",
  2694  		"system-data/etc/machine-id-except-not",
  2695  	}
  2696  	for _, mockFile := range append(mockCopiedFiles, mockUnrelatedFiles...) {
  2697  		p := filepath.Join(hostUbuntuData, mockFile)
  2698  		err = os.MkdirAll(filepath.Dir(p), 0750)
  2699  		c.Assert(err, IsNil)
  2700  		mockContent := fmt.Sprintf("content of %s", filepath.Base(mockFile))
  2701  		err = ioutil.WriteFile(p, []byte(mockContent), 0640)
  2702  		c.Assert(err, IsNil)
  2703  	}
  2704  	// create a mock state
  2705  	mockedState := filepath.Join(hostUbuntuData, "system-data/var/lib/snapd/state.json")
  2706  	err = os.MkdirAll(filepath.Dir(mockedState), 0750)
  2707  	c.Assert(err, IsNil)
  2708  	err = ioutil.WriteFile(mockedState, []byte(mockStateContent), 0640)
  2709  	c.Assert(err, IsNil)
  2710  
  2711  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  2712  	c.Assert(err, IsNil)
  2713  
  2714  	// we always need to lock access to sealed keys
  2715  	c.Check(sealedKeysLocked, Equals, true)
  2716  
  2717  	modeEnv := filepath.Join(ephemeralUbuntuData, "/system-data/var/lib/snapd/modeenv")
  2718  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  2719  recovery_system=20191118
  2720  base=core20_1.snap
  2721  model=my-brand/my-model
  2722  grade=signed
  2723  `)
  2724  	for _, p := range mockUnrelatedFiles {
  2725  		c.Check(filepath.Join(ephemeralUbuntuData, p), testutil.FileAbsent)
  2726  	}
  2727  	for _, p := range mockCopiedFiles {
  2728  		c.Check(filepath.Join(ephemeralUbuntuData, p), testutil.FilePresent)
  2729  		fi, err := os.Stat(filepath.Join(ephemeralUbuntuData, p))
  2730  		// check file mode is set
  2731  		c.Assert(err, IsNil)
  2732  		c.Check(fi.Mode(), Equals, os.FileMode(0640))
  2733  		// check dir mode is set in parent dir
  2734  		fiParent, err := os.Stat(filepath.Dir(filepath.Join(ephemeralUbuntuData, p)))
  2735  		c.Assert(err, IsNil)
  2736  		c.Check(fiParent.Mode(), Equals, os.FileMode(os.ModeDir|0750))
  2737  	}
  2738  
  2739  	c.Check(filepath.Join(ephemeralUbuntuData, "system-data/var/lib/snapd/state.json"), testutil.FileEquals, `{"data":{"auth":{"last-id":1,"macaroon-key":"not-a-cookie","users":[{"id":1,"name":"mvo"}]}},"changes":{},"tasks":{},"last-change-id":0,"last-task-id":0,"last-lane-id":0}`)
  2740  
  2741  	// finally check that the recovery system bootenv was updated to be in run
  2742  	// mode
  2743  	bloader, err := bootloader.Find("", nil)
  2744  	c.Assert(err, IsNil)
  2745  	m, err := bloader.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  2746  	c.Assert(err, IsNil)
  2747  	c.Assert(m, DeepEquals, map[string]string{
  2748  		"snapd_recovery_system": "20191118",
  2749  		"snapd_recovery_mode":   "run",
  2750  	})
  2751  }
  2752  
  2753  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappy(c *C) {
  2754  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  2755  
  2756  	// setup a bootloader for setting the bootenv after we are done
  2757  	bloader := bootloadertest.Mock("mock", c.MkDir())
  2758  	bootloader.Force(bloader)
  2759  	defer bootloader.Force(nil)
  2760  
  2761  	// mock that we don't know which partition uuid the kernel was booted from
  2762  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  2763  	defer restore()
  2764  
  2765  	restore = disks.MockMountPointDisksToPartitionMapping(
  2766  		map[disks.Mountpoint]*disks.MockDiskMapping{
  2767  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  2768  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  2769  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  2770  			{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  2771  		},
  2772  	)
  2773  	defer restore()
  2774  
  2775  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  2776  		ubuntuLabelMount("ubuntu-seed", "recover"),
  2777  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  2778  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  2779  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  2780  		{
  2781  			"tmpfs",
  2782  			boot.InitramfsDataDir,
  2783  			tmpfsMountOpts,
  2784  		},
  2785  		{
  2786  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  2787  			boot.InitramfsUbuntuBootDir,
  2788  			needsFsckDiskMountOpts,
  2789  		},
  2790  		{
  2791  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  2792  			boot.InitramfsHostUbuntuDataDir,
  2793  			needsNoSuidDiskMountOpts,
  2794  		},
  2795  		{
  2796  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  2797  			boot.InitramfsUbuntuSaveDir,
  2798  			nil,
  2799  		},
  2800  	}, nil)
  2801  	defer restore()
  2802  
  2803  	s.testRecoverModeHappy(c)
  2804  
  2805  	// we should not have written a degraded.json
  2806  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  2807  
  2808  	// we also should have written an empty boot-flags file
  2809  	c.Assert(filepath.Join(dirs.SnapRunDir, "boot-flags"), testutil.FileEquals, "")
  2810  }
  2811  
  2812  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeTimeMovesForwardHappy(c *C) {
  2813  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  2814  
  2815  	for _, tc := range s.timeTestCases() {
  2816  		comment := Commentf(tc.comment)
  2817  		cleanups := []func(){}
  2818  
  2819  		// always remove the ubuntu-seed dir, otherwise setupSeed complains the
  2820  		// model file already exists and can't setup the seed
  2821  		err := os.RemoveAll(filepath.Join(boot.InitramfsUbuntuSeedDir))
  2822  		c.Assert(err, IsNil, comment)
  2823  
  2824  		// also always remove the data dir, since we need to copy state.json
  2825  		// there, so if the file already exists the initramfs code dies
  2826  		err = os.RemoveAll(filepath.Join(boot.InitramfsDataDir))
  2827  		c.Assert(err, IsNil, comment)
  2828  
  2829  		s.setupSeed(c, tc.modelTime, nil)
  2830  
  2831  		restore := main.MockTimeNow(func() time.Time {
  2832  			return tc.now
  2833  		})
  2834  		cleanups = append(cleanups, restore)
  2835  
  2836  		restore = disks.MockMountPointDisksToPartitionMapping(
  2837  			map[disks.Mountpoint]*disks.MockDiskMapping{
  2838  				{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  2839  				{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  2840  				{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  2841  				{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  2842  			},
  2843  		)
  2844  		cleanups = append(cleanups, restore)
  2845  		osutilSetTimeCalls := 0
  2846  		// check what time we try to move forward to
  2847  		restore = main.MockOsutilSetTime(func(t time.Time) error {
  2848  			osutilSetTimeCalls++
  2849  			// make sure the timestamps are within 1 second of each other, they
  2850  			// won't be equal since the timestamp is serialized to an assertion and
  2851  			// read back
  2852  			tTrunc := t.Truncate(2 * time.Second)
  2853  			expTTrunc := tc.expT.Truncate(2 * time.Second)
  2854  			c.Assert(tTrunc.Equal(expTTrunc), Equals, true, Commentf("%s, exp %s, got %s", tc.comment, t, s.snapDeclAssertsTime))
  2855  			return nil
  2856  		})
  2857  		cleanups = append(cleanups, restore)
  2858  
  2859  		restore = s.mockSystemdMountSequence(c, []systemdMount{
  2860  			ubuntuLabelMount("ubuntu-seed", "recover"),
  2861  			s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  2862  			s.makeSeedSnapSystemdMount(snap.TypeKernel),
  2863  			s.makeSeedSnapSystemdMount(snap.TypeBase),
  2864  			{
  2865  				"tmpfs",
  2866  				boot.InitramfsDataDir,
  2867  				tmpfsMountOpts,
  2868  			},
  2869  			{
  2870  				"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  2871  				boot.InitramfsUbuntuBootDir,
  2872  				needsFsckDiskMountOpts,
  2873  			},
  2874  			{
  2875  				"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  2876  				boot.InitramfsHostUbuntuDataDir,
  2877  				needsNoSuidDiskMountOpts,
  2878  			},
  2879  			{
  2880  				"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  2881  				boot.InitramfsUbuntuSaveDir,
  2882  				nil,
  2883  			},
  2884  		}, nil)
  2885  		cleanups = append(cleanups, restore)
  2886  
  2887  		bloader := bootloadertest.Mock("mock", c.MkDir())
  2888  		bootloader.Force(bloader)
  2889  		cleanups = append(cleanups, func() { bootloader.Force(nil) })
  2890  
  2891  		s.testRecoverModeHappy(c)
  2892  		c.Assert(osutilSetTimeCalls, Equals, tc.setTimeCalls)
  2893  
  2894  		for _, r := range cleanups {
  2895  			r()
  2896  		}
  2897  	}
  2898  }
  2899  
  2900  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeGadgetDefaultsHappy(c *C) {
  2901  	// setup a seed with default gadget yaml
  2902  	const gadgetYamlDefaults = `
  2903  defaults:
  2904    system:
  2905      service:
  2906        rsyslog.disable: true
  2907        ssh.disable: true
  2908        console-conf.disable: true
  2909      journal.persistent: true
  2910  `
  2911  	c.Assert(os.RemoveAll(s.seedDir), IsNil)
  2912  
  2913  	s.setupSeed(c, time.Time{}, [][]string{
  2914  		{"meta/gadget.yaml", gadgetYamlDefaults},
  2915  	})
  2916  
  2917  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  2918  
  2919  	// setup a bootloader for setting the bootenv after we are done
  2920  	bloader := bootloadertest.Mock("mock", c.MkDir())
  2921  	bootloader.Force(bloader)
  2922  	defer bootloader.Force(nil)
  2923  
  2924  	// mock that we don't know which partition uuid the kernel was booted from
  2925  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  2926  	defer restore()
  2927  
  2928  	restore = disks.MockMountPointDisksToPartitionMapping(
  2929  		map[disks.Mountpoint]*disks.MockDiskMapping{
  2930  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  2931  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  2932  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  2933  			{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  2934  		},
  2935  	)
  2936  	defer restore()
  2937  
  2938  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  2939  		ubuntuLabelMount("ubuntu-seed", "recover"),
  2940  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  2941  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  2942  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  2943  		{
  2944  			"tmpfs",
  2945  			boot.InitramfsDataDir,
  2946  			tmpfsMountOpts,
  2947  		},
  2948  		{
  2949  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  2950  			boot.InitramfsUbuntuBootDir,
  2951  			needsFsckDiskMountOpts,
  2952  		},
  2953  		{
  2954  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  2955  			boot.InitramfsHostUbuntuDataDir,
  2956  			needsNoSuidDiskMountOpts,
  2957  		},
  2958  		{
  2959  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  2960  			boot.InitramfsUbuntuSaveDir,
  2961  			nil,
  2962  		},
  2963  	}, nil)
  2964  	defer restore()
  2965  
  2966  	// we will call out to systemctl in the initramfs, but only using --root
  2967  	// which doesn't talk to systemd, just manipulates files around
  2968  	var sysctlArgs [][]string
  2969  	systemctlRestorer := systemd.MockSystemctl(func(args ...string) (buf []byte, err error) {
  2970  		sysctlArgs = append(sysctlArgs, args)
  2971  		return nil, nil
  2972  	})
  2973  	defer systemctlRestorer()
  2974  
  2975  	s.testRecoverModeHappy(c)
  2976  
  2977  	// we should not have written a degraded.json
  2978  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  2979  
  2980  	c.Assert(osutil.FileExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/cloud/cloud-init.disabled")), Equals, true)
  2981  
  2982  	// check that everything from the gadget defaults was setup
  2983  	c.Assert(osutil.FileExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/ssh/sshd_not_to_be_run")), Equals, true)
  2984  	c.Assert(osutil.FileExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/var/lib/console-conf/complete")), Equals, true)
  2985  	exists, _, _ := osutil.DirExists(filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/var/log/journal"))
  2986  	c.Assert(exists, Equals, true)
  2987  
  2988  	// systemctl was called the way we expect
  2989  	c.Assert(sysctlArgs, DeepEquals, [][]string{{"--root", filepath.Join(boot.InitramfsWritableDir, "_writable_defaults"), "mask", "rsyslog.service"}})
  2990  }
  2991  
  2992  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappyBootedKernelPartitionUUID(c *C) {
  2993  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  2994  
  2995  	restore := main.MockPartitionUUIDForBootedKernelDisk("specific-ubuntu-seed-partuuid")
  2996  	defer restore()
  2997  
  2998  	// setup a bootloader for setting the bootenv after we are done
  2999  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3000  	bootloader.Force(bloader)
  3001  	defer bootloader.Force(nil)
  3002  
  3003  	restore = disks.MockMountPointDisksToPartitionMapping(
  3004  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3005  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  3006  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  3007  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  3008  			{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  3009  		},
  3010  	)
  3011  	defer restore()
  3012  
  3013  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3014  		{
  3015  			"/dev/disk/by-partuuid/specific-ubuntu-seed-partuuid",
  3016  			boot.InitramfsUbuntuSeedDir,
  3017  			needsFsckDiskMountOpts,
  3018  		},
  3019  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3020  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3021  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3022  		{
  3023  			"tmpfs",
  3024  			boot.InitramfsDataDir,
  3025  			tmpfsMountOpts,
  3026  		},
  3027  		{
  3028  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3029  			boot.InitramfsUbuntuBootDir,
  3030  			needsFsckDiskMountOpts,
  3031  		},
  3032  		{
  3033  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  3034  			boot.InitramfsHostUbuntuDataDir,
  3035  			needsNoSuidDiskMountOpts,
  3036  		},
  3037  		{
  3038  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  3039  			boot.InitramfsUbuntuSaveDir,
  3040  			nil,
  3041  		},
  3042  	}, nil)
  3043  	defer restore()
  3044  
  3045  	s.testRecoverModeHappy(c)
  3046  
  3047  	// we should not have written a degraded.json
  3048  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  3049  }
  3050  
  3051  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappyEncrypted(c *C) {
  3052  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  3053  
  3054  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  3055  	defer restore()
  3056  
  3057  	// setup a bootloader for setting the bootenv after we are done
  3058  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3059  	bootloader.Force(bloader)
  3060  	defer bootloader.Force(nil)
  3061  
  3062  	restore = disks.MockMountPointDisksToPartitionMapping(
  3063  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3064  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  3065  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  3066  			{
  3067  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  3068  				IsDecryptedDevice: true,
  3069  			}: defaultEncBootDisk,
  3070  			{
  3071  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  3072  				IsDecryptedDevice: true,
  3073  			}: defaultEncBootDisk,
  3074  		},
  3075  	)
  3076  	defer restore()
  3077  
  3078  	dataActivated := false
  3079  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  3080  		c.Assert(name, Equals, "ubuntu-data")
  3081  		c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  3082  
  3083  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3084  		c.Assert(err, IsNil)
  3085  		c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3086  		c.Assert(opts.AllowRecoveryKey, Equals, false)
  3087  		c.Assert(opts.WhichModel, NotNil)
  3088  		mod, err := opts.WhichModel()
  3089  		c.Assert(err, IsNil)
  3090  		c.Check(mod.Model(), Equals, "my-model")
  3091  
  3092  		dataActivated = true
  3093  		return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  3094  	})
  3095  	defer restore()
  3096  
  3097  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  3098  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  3099  
  3100  	saveActivated := false
  3101  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  3102  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  3103  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3104  		c.Assert(err, IsNil)
  3105  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3106  		c.Assert(key, DeepEquals, []byte("foo"))
  3107  		saveActivated = true
  3108  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  3109  	})
  3110  	defer restore()
  3111  
  3112  	measureEpochCalls := 0
  3113  	measureModelCalls := 0
  3114  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  3115  		measureEpochCalls++
  3116  		return nil
  3117  	})
  3118  	defer restore()
  3119  
  3120  	var measuredModel *asserts.Model
  3121  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  3122  		measureModelCalls++
  3123  		var err error
  3124  		measuredModel, err = findModel()
  3125  		if err != nil {
  3126  			return err
  3127  		}
  3128  		return nil
  3129  	})
  3130  	defer restore()
  3131  
  3132  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3133  		ubuntuLabelMount("ubuntu-seed", "recover"),
  3134  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3135  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3136  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3137  		{
  3138  			"tmpfs",
  3139  			boot.InitramfsDataDir,
  3140  			tmpfsMountOpts,
  3141  		},
  3142  		{
  3143  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3144  			boot.InitramfsUbuntuBootDir,
  3145  			needsFsckDiskMountOpts,
  3146  		},
  3147  		{
  3148  			"/dev/mapper/ubuntu-data-random",
  3149  			boot.InitramfsHostUbuntuDataDir,
  3150  			needsNoSuidDiskMountOpts,
  3151  		},
  3152  		{
  3153  			"/dev/mapper/ubuntu-save-random",
  3154  			boot.InitramfsUbuntuSaveDir,
  3155  			nil,
  3156  		},
  3157  	}, nil)
  3158  	defer restore()
  3159  
  3160  	s.testRecoverModeHappy(c)
  3161  
  3162  	// we should not have written a degraded.json
  3163  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  3164  
  3165  	c.Check(dataActivated, Equals, true)
  3166  	c.Check(saveActivated, Equals, true)
  3167  	c.Check(measureEpochCalls, Equals, 1)
  3168  	c.Check(measureModelCalls, Equals, 1)
  3169  	c.Check(measuredModel, DeepEquals, s.model)
  3170  
  3171  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  3172  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  3173  }
  3174  
  3175  func checkDegradedJSON(c *C, exp map[string]interface{}) {
  3176  	b, err := ioutil.ReadFile(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"))
  3177  	c.Assert(err, IsNil)
  3178  	degradedJSONObj := make(map[string]interface{})
  3179  	err = json.Unmarshal(b, &degradedJSONObj)
  3180  	c.Assert(err, IsNil)
  3181  
  3182  	c.Assert(degradedJSONObj, DeepEquals, exp)
  3183  }
  3184  
  3185  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedDataUnlockFallbackHappy(c *C) {
  3186  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  3187  
  3188  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  3189  	defer restore()
  3190  
  3191  	// setup a bootloader for setting the bootenv after we are done
  3192  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3193  	bootloader.Force(bloader)
  3194  	defer bootloader.Force(nil)
  3195  
  3196  	restore = disks.MockMountPointDisksToPartitionMapping(
  3197  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3198  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  3199  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  3200  			{
  3201  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  3202  				IsDecryptedDevice: true,
  3203  			}: defaultEncBootDisk,
  3204  			{
  3205  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  3206  				IsDecryptedDevice: true,
  3207  			}: defaultEncBootDisk,
  3208  		},
  3209  	)
  3210  	defer restore()
  3211  
  3212  	dataActivated := false
  3213  	saveActivated := false
  3214  	unlockVolumeWithSealedKeyCalls := 0
  3215  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  3216  		unlockVolumeWithSealedKeyCalls++
  3217  		switch unlockVolumeWithSealedKeyCalls {
  3218  
  3219  		case 1:
  3220  			// pretend we can't unlock ubuntu-data with the main run key
  3221  			c.Assert(name, Equals, "ubuntu-data")
  3222  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  3223  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3224  			c.Assert(err, IsNil)
  3225  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3226  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  3227  			c.Assert(opts.WhichModel, NotNil)
  3228  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data")
  3229  
  3230  		case 2:
  3231  			// now we can unlock ubuntu-data with the fallback key
  3232  			c.Assert(name, Equals, "ubuntu-data")
  3233  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"))
  3234  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3235  			c.Assert(err, IsNil)
  3236  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3237  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  3238  			c.Assert(opts.WhichModel, NotNil)
  3239  			mod, err := opts.WhichModel()
  3240  			c.Assert(err, IsNil)
  3241  			c.Check(mod.Model(), Equals, "my-model")
  3242  
  3243  			dataActivated = true
  3244  			return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  3245  		default:
  3246  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  3247  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  3248  		}
  3249  	})
  3250  	defer restore()
  3251  
  3252  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  3253  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  3254  
  3255  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  3256  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  3257  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3258  		c.Assert(err, IsNil)
  3259  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3260  		c.Assert(key, DeepEquals, []byte("foo"))
  3261  		saveActivated = true
  3262  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  3263  	})
  3264  	defer restore()
  3265  
  3266  	measureEpochCalls := 0
  3267  	measureModelCalls := 0
  3268  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  3269  		measureEpochCalls++
  3270  		return nil
  3271  	})
  3272  	defer restore()
  3273  
  3274  	var measuredModel *asserts.Model
  3275  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  3276  		measureModelCalls++
  3277  		var err error
  3278  		measuredModel, err = findModel()
  3279  		if err != nil {
  3280  			return err
  3281  		}
  3282  		return nil
  3283  	})
  3284  	defer restore()
  3285  
  3286  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3287  		ubuntuLabelMount("ubuntu-seed", "recover"),
  3288  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3289  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3290  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3291  		{
  3292  			"tmpfs",
  3293  			boot.InitramfsDataDir,
  3294  			tmpfsMountOpts,
  3295  		},
  3296  		{
  3297  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3298  			boot.InitramfsUbuntuBootDir,
  3299  			needsFsckDiskMountOpts,
  3300  		},
  3301  		{
  3302  			"/dev/mapper/ubuntu-data-random",
  3303  			boot.InitramfsHostUbuntuDataDir,
  3304  			needsNoSuidDiskMountOpts,
  3305  		},
  3306  		{
  3307  			"/dev/mapper/ubuntu-save-random",
  3308  			boot.InitramfsUbuntuSaveDir,
  3309  			nil,
  3310  		},
  3311  	}, nil)
  3312  	defer restore()
  3313  
  3314  	s.testRecoverModeHappy(c)
  3315  
  3316  	checkDegradedJSON(c, map[string]interface{}{
  3317  		"ubuntu-boot": map[string]interface{}{
  3318  			"find-state":     "found",
  3319  			"mount-state":    "mounted",
  3320  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3321  			"mount-location": boot.InitramfsUbuntuBootDir,
  3322  		},
  3323  		"ubuntu-data": map[string]interface{}{
  3324  			"device":         "/dev/mapper/ubuntu-data-random",
  3325  			"unlock-state":   "unlocked",
  3326  			"find-state":     "found",
  3327  			"mount-state":    "mounted",
  3328  			"unlock-key":     "fallback",
  3329  			"mount-location": boot.InitramfsHostUbuntuDataDir,
  3330  		},
  3331  		"ubuntu-save": map[string]interface{}{
  3332  			"device":         "/dev/mapper/ubuntu-save-random",
  3333  			"unlock-key":     "run",
  3334  			"unlock-state":   "unlocked",
  3335  			"mount-state":    "mounted",
  3336  			"find-state":     "found",
  3337  			"mount-location": boot.InitramfsUbuntuSaveDir,
  3338  		},
  3339  		"error-log": []interface{}{
  3340  			"cannot unlock encrypted ubuntu-data (device /dev/disk/by-partuuid/ubuntu-data-enc-partuuid) with sealed run key: failed to unlock ubuntu-data",
  3341  		},
  3342  	})
  3343  
  3344  	c.Check(dataActivated, Equals, true)
  3345  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 2)
  3346  	c.Check(saveActivated, Equals, true)
  3347  	c.Check(measureEpochCalls, Equals, 1)
  3348  	c.Check(measureModelCalls, Equals, 1)
  3349  	c.Check(measuredModel, DeepEquals, s.model)
  3350  
  3351  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  3352  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  3353  }
  3354  
  3355  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedSaveUnlockFallbackHappy(c *C) {
  3356  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  3357  
  3358  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  3359  	defer restore()
  3360  
  3361  	// setup a bootloader for setting the bootenv after we are done
  3362  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3363  	bootloader.Force(bloader)
  3364  	defer bootloader.Force(nil)
  3365  
  3366  	restore = disks.MockMountPointDisksToPartitionMapping(
  3367  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3368  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  3369  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  3370  			{
  3371  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  3372  				IsDecryptedDevice: true,
  3373  			}: defaultEncBootDisk,
  3374  			{
  3375  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  3376  				IsDecryptedDevice: true,
  3377  			}: defaultEncBootDisk,
  3378  		},
  3379  	)
  3380  	defer restore()
  3381  
  3382  	dataActivated := false
  3383  	saveActivationAttempted := false
  3384  	unlockVolumeWithSealedKeyCalls := 0
  3385  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  3386  		unlockVolumeWithSealedKeyCalls++
  3387  		switch unlockVolumeWithSealedKeyCalls {
  3388  
  3389  		case 1:
  3390  			// ubuntu data can be unlocked fine
  3391  			c.Assert(name, Equals, "ubuntu-data")
  3392  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  3393  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3394  			c.Assert(err, IsNil)
  3395  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3396  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  3397  			c.Assert(opts.WhichModel, NotNil)
  3398  			dataActivated = true
  3399  			return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  3400  
  3401  		case 2:
  3402  			// then after ubuntu-save is attempted to be unlocked with the
  3403  			// unsealed run object on the encrypted data partition, we fall back
  3404  			// to using the sealed object on ubuntu-seed for save
  3405  			c.Assert(saveActivationAttempted, Equals, true)
  3406  			c.Assert(name, Equals, "ubuntu-save")
  3407  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"))
  3408  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3409  			c.Assert(err, IsNil)
  3410  			c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3411  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  3412  			c.Assert(opts.WhichModel, NotNil)
  3413  			mod, err := opts.WhichModel()
  3414  			c.Assert(err, IsNil)
  3415  			c.Check(mod.Model(), Equals, "my-model")
  3416  			dataActivated = true
  3417  			return happyUnlocked("ubuntu-save", secboot.UnlockedWithSealedKey), nil
  3418  		default:
  3419  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  3420  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  3421  		}
  3422  	})
  3423  	defer restore()
  3424  
  3425  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  3426  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  3427  
  3428  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  3429  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  3430  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3431  		c.Assert(err, IsNil)
  3432  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3433  		c.Assert(key, DeepEquals, []byte("foo"))
  3434  		saveActivationAttempted = true
  3435  		return foundEncrypted("ubuntu-save"), fmt.Errorf("failed to unlock ubuntu-save with run object")
  3436  	})
  3437  	defer restore()
  3438  
  3439  	measureEpochCalls := 0
  3440  	measureModelCalls := 0
  3441  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  3442  		measureEpochCalls++
  3443  		return nil
  3444  	})
  3445  	defer restore()
  3446  
  3447  	var measuredModel *asserts.Model
  3448  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  3449  		measureModelCalls++
  3450  		var err error
  3451  		measuredModel, err = findModel()
  3452  		if err != nil {
  3453  			return err
  3454  		}
  3455  		return nil
  3456  	})
  3457  	defer restore()
  3458  
  3459  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3460  		ubuntuLabelMount("ubuntu-seed", "recover"),
  3461  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3462  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3463  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3464  		{
  3465  			"tmpfs",
  3466  			boot.InitramfsDataDir,
  3467  			tmpfsMountOpts,
  3468  		},
  3469  		{
  3470  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3471  			boot.InitramfsUbuntuBootDir,
  3472  			needsFsckDiskMountOpts,
  3473  		},
  3474  		{
  3475  			"/dev/mapper/ubuntu-data-random",
  3476  			boot.InitramfsHostUbuntuDataDir,
  3477  			needsNoSuidDiskMountOpts,
  3478  		},
  3479  		{
  3480  			"/dev/mapper/ubuntu-save-random",
  3481  			boot.InitramfsUbuntuSaveDir,
  3482  			nil,
  3483  		},
  3484  	}, nil)
  3485  	defer restore()
  3486  
  3487  	s.testRecoverModeHappy(c)
  3488  
  3489  	checkDegradedJSON(c, map[string]interface{}{
  3490  		"ubuntu-boot": map[string]interface{}{
  3491  			"find-state":     "found",
  3492  			"mount-state":    "mounted",
  3493  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3494  			"mount-location": boot.InitramfsUbuntuBootDir,
  3495  		},
  3496  		"ubuntu-data": map[string]interface{}{
  3497  			"device":         "/dev/mapper/ubuntu-data-random",
  3498  			"unlock-state":   "unlocked",
  3499  			"find-state":     "found",
  3500  			"mount-state":    "mounted",
  3501  			"unlock-key":     "run",
  3502  			"mount-location": boot.InitramfsHostUbuntuDataDir,
  3503  		},
  3504  		"ubuntu-save": map[string]interface{}{
  3505  			"device":         "/dev/mapper/ubuntu-save-random",
  3506  			"unlock-key":     "fallback",
  3507  			"unlock-state":   "unlocked",
  3508  			"mount-state":    "mounted",
  3509  			"find-state":     "found",
  3510  			"mount-location": boot.InitramfsUbuntuSaveDir,
  3511  		},
  3512  		"error-log": []interface{}{
  3513  			"cannot unlock encrypted ubuntu-save (device /dev/disk/by-partuuid/ubuntu-save-enc-partuuid) with sealed run key: failed to unlock ubuntu-save with run object",
  3514  		},
  3515  	})
  3516  
  3517  	c.Check(dataActivated, Equals, true)
  3518  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 2)
  3519  	c.Check(saveActivationAttempted, Equals, true)
  3520  	c.Check(measureEpochCalls, Equals, 1)
  3521  	c.Check(measureModelCalls, Equals, 1)
  3522  	c.Check(measuredModel, DeepEquals, s.model)
  3523  
  3524  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  3525  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  3526  }
  3527  
  3528  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedAbsentBootDataUnlockFallbackHappy(c *C) {
  3529  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  3530  
  3531  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  3532  	defer restore()
  3533  
  3534  	// setup a bootloader for setting the bootenv after we are done
  3535  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3536  	bootloader.Force(bloader)
  3537  	defer bootloader.Force(nil)
  3538  
  3539  	defaultEncDiskNoBoot := &disks.MockDiskMapping{
  3540  		FilesystemLabelToPartUUID: map[string]string{
  3541  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  3542  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
  3543  			"ubuntu-save-enc": "ubuntu-save-enc-partuuid",
  3544  		},
  3545  		DiskHasPartitions: true,
  3546  		DevNum:            "defaultEncDevNoBoot",
  3547  	}
  3548  
  3549  	restore = disks.MockMountPointDisksToPartitionMapping(
  3550  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3551  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncDiskNoBoot,
  3552  			// no ubuntu-boot so we fall back to unlocking data with fallback
  3553  			// key right away
  3554  			{
  3555  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  3556  				IsDecryptedDevice: true,
  3557  			}: defaultEncDiskNoBoot,
  3558  			{
  3559  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  3560  				IsDecryptedDevice: true,
  3561  			}: defaultEncDiskNoBoot,
  3562  		},
  3563  	)
  3564  	defer restore()
  3565  
  3566  	dataActivated := false
  3567  	unlockVolumeWithSealedKeyCalls := 0
  3568  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  3569  		unlockVolumeWithSealedKeyCalls++
  3570  		switch unlockVolumeWithSealedKeyCalls {
  3571  		case 1:
  3572  			// we skip trying to unlock with run key on ubuntu-boot and go
  3573  			// directly to using the fallback key on ubuntu-seed
  3574  			c.Assert(name, Equals, "ubuntu-data")
  3575  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"))
  3576  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3577  			c.Assert(err, IsNil)
  3578  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3579  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  3580  			c.Assert(opts.WhichModel, NotNil)
  3581  			dataActivated = true
  3582  			return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  3583  		default:
  3584  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  3585  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  3586  		}
  3587  	})
  3588  	defer restore()
  3589  
  3590  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  3591  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  3592  
  3593  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  3594  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  3595  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3596  		c.Assert(err, IsNil)
  3597  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3598  		c.Assert(key, DeepEquals, []byte("foo"))
  3599  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  3600  	})
  3601  	defer restore()
  3602  
  3603  	measureEpochCalls := 0
  3604  	measureModelCalls := 0
  3605  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  3606  		measureEpochCalls++
  3607  		return nil
  3608  	})
  3609  	defer restore()
  3610  
  3611  	var measuredModel *asserts.Model
  3612  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  3613  		measureModelCalls++
  3614  		var err error
  3615  		measuredModel, err = findModel()
  3616  		if err != nil {
  3617  			return err
  3618  		}
  3619  		return nil
  3620  	})
  3621  	defer restore()
  3622  
  3623  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3624  		ubuntuLabelMount("ubuntu-seed", "recover"),
  3625  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3626  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3627  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3628  		{
  3629  			"tmpfs",
  3630  			boot.InitramfsDataDir,
  3631  			tmpfsMountOpts,
  3632  		},
  3633  		// no ubuntu-boot
  3634  		{
  3635  			"/dev/mapper/ubuntu-data-random",
  3636  			boot.InitramfsHostUbuntuDataDir,
  3637  			needsNoSuidDiskMountOpts,
  3638  		},
  3639  		{
  3640  			"/dev/mapper/ubuntu-save-random",
  3641  			boot.InitramfsUbuntuSaveDir,
  3642  			nil,
  3643  		},
  3644  	}, nil)
  3645  	defer restore()
  3646  
  3647  	s.testRecoverModeHappy(c)
  3648  
  3649  	checkDegradedJSON(c, map[string]interface{}{
  3650  		"ubuntu-boot": map[string]interface{}{
  3651  			"find-state": "not-found",
  3652  		},
  3653  		"ubuntu-data": map[string]interface{}{
  3654  			"device":         "/dev/mapper/ubuntu-data-random",
  3655  			"unlock-state":   "unlocked",
  3656  			"find-state":     "found",
  3657  			"mount-state":    "mounted",
  3658  			"unlock-key":     "fallback",
  3659  			"mount-location": boot.InitramfsHostUbuntuDataDir,
  3660  		},
  3661  		"ubuntu-save": map[string]interface{}{
  3662  			"device":         "/dev/mapper/ubuntu-save-random",
  3663  			"unlock-key":     "run",
  3664  			"unlock-state":   "unlocked",
  3665  			"mount-state":    "mounted",
  3666  			"find-state":     "found",
  3667  			"mount-location": boot.InitramfsUbuntuSaveDir,
  3668  		},
  3669  		"error-log": []interface{}{
  3670  			"cannot find ubuntu-boot partition on disk defaultEncDevNoBoot",
  3671  		},
  3672  	})
  3673  
  3674  	c.Check(dataActivated, Equals, true)
  3675  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 1)
  3676  	c.Check(measureEpochCalls, Equals, 1)
  3677  	c.Check(measureModelCalls, Equals, 1)
  3678  	c.Check(measuredModel, DeepEquals, s.model)
  3679  
  3680  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  3681  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  3682  }
  3683  
  3684  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedAbsentBootDataUnlockRecoveryKeyHappy(c *C) {
  3685  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  3686  
  3687  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  3688  	defer restore()
  3689  
  3690  	// setup a bootloader for setting the bootenv after we are done
  3691  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3692  	bootloader.Force(bloader)
  3693  	defer bootloader.Force(nil)
  3694  
  3695  	defaultEncDiskNoBoot := &disks.MockDiskMapping{
  3696  		FilesystemLabelToPartUUID: map[string]string{
  3697  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  3698  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
  3699  			"ubuntu-save-enc": "ubuntu-save-enc-partuuid",
  3700  		},
  3701  		DiskHasPartitions: true,
  3702  		DevNum:            "defaultEncDevNoBoot",
  3703  	}
  3704  
  3705  	restore = disks.MockMountPointDisksToPartitionMapping(
  3706  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3707  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncDiskNoBoot,
  3708  			// no ubuntu-boot so we fall back to unlocking data with fallback
  3709  			// key right away
  3710  			{
  3711  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  3712  				IsDecryptedDevice: true,
  3713  			}: defaultEncDiskNoBoot,
  3714  			{
  3715  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  3716  				IsDecryptedDevice: true,
  3717  			}: defaultEncDiskNoBoot,
  3718  		},
  3719  	)
  3720  	defer restore()
  3721  
  3722  	dataActivated := false
  3723  	unlockVolumeWithSealedKeyCalls := 0
  3724  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  3725  		unlockVolumeWithSealedKeyCalls++
  3726  		switch unlockVolumeWithSealedKeyCalls {
  3727  		case 1:
  3728  			// we skip trying to unlock with run key on ubuntu-boot and go
  3729  			// directly to using the fallback key on ubuntu-seed
  3730  			c.Assert(name, Equals, "ubuntu-data")
  3731  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"))
  3732  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3733  			c.Assert(err, IsNil)
  3734  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3735  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  3736  			c.Assert(opts.WhichModel, NotNil)
  3737  			dataActivated = true
  3738  			// it was unlocked with a recovery key
  3739  
  3740  			return happyUnlocked("ubuntu-data", secboot.UnlockedWithRecoveryKey), nil
  3741  		default:
  3742  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  3743  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  3744  		}
  3745  	})
  3746  	defer restore()
  3747  
  3748  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  3749  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  3750  
  3751  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  3752  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  3753  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3754  		c.Assert(err, IsNil)
  3755  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3756  		c.Assert(key, DeepEquals, []byte("foo"))
  3757  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  3758  	})
  3759  	defer restore()
  3760  
  3761  	measureEpochCalls := 0
  3762  	measureModelCalls := 0
  3763  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  3764  		measureEpochCalls++
  3765  		return nil
  3766  	})
  3767  	defer restore()
  3768  
  3769  	var measuredModel *asserts.Model
  3770  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  3771  		measureModelCalls++
  3772  		var err error
  3773  		measuredModel, err = findModel()
  3774  		if err != nil {
  3775  			return err
  3776  		}
  3777  		return nil
  3778  	})
  3779  	defer restore()
  3780  
  3781  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3782  		ubuntuLabelMount("ubuntu-seed", "recover"),
  3783  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3784  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3785  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3786  		{
  3787  			"tmpfs",
  3788  			boot.InitramfsDataDir,
  3789  			tmpfsMountOpts,
  3790  		},
  3791  		// no ubuntu-boot
  3792  		{
  3793  			"/dev/mapper/ubuntu-data-random",
  3794  			boot.InitramfsHostUbuntuDataDir,
  3795  			needsNoSuidDiskMountOpts,
  3796  		},
  3797  		{
  3798  			"/dev/mapper/ubuntu-save-random",
  3799  			boot.InitramfsUbuntuSaveDir,
  3800  			nil,
  3801  		},
  3802  	}, nil)
  3803  	defer restore()
  3804  
  3805  	s.testRecoverModeHappy(c)
  3806  
  3807  	checkDegradedJSON(c, map[string]interface{}{
  3808  		"ubuntu-boot": map[string]interface{}{
  3809  			"find-state": "not-found",
  3810  		},
  3811  		"ubuntu-data": map[string]interface{}{
  3812  			"device":         "/dev/mapper/ubuntu-data-random",
  3813  			"unlock-state":   "unlocked",
  3814  			"find-state":     "found",
  3815  			"mount-state":    "mounted",
  3816  			"unlock-key":     "recovery",
  3817  			"mount-location": boot.InitramfsHostUbuntuDataDir,
  3818  		},
  3819  		"ubuntu-save": map[string]interface{}{
  3820  			"device":         "/dev/mapper/ubuntu-save-random",
  3821  			"unlock-key":     "run",
  3822  			"unlock-state":   "unlocked",
  3823  			"mount-state":    "mounted",
  3824  			"find-state":     "found",
  3825  			"mount-location": boot.InitramfsUbuntuSaveDir,
  3826  		},
  3827  		"error-log": []interface{}{
  3828  			"cannot find ubuntu-boot partition on disk defaultEncDevNoBoot",
  3829  		},
  3830  	})
  3831  
  3832  	c.Check(dataActivated, Equals, true)
  3833  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 1)
  3834  	c.Check(measureEpochCalls, Equals, 1)
  3835  	c.Check(measureModelCalls, Equals, 1)
  3836  	c.Check(measuredModel, DeepEquals, s.model)
  3837  
  3838  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  3839  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  3840  }
  3841  
  3842  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedDataUnlockFailSaveUnlockFallbackHappy(c *C) {
  3843  	// test a scenario when unsealing of data fails with both the run key
  3844  	// and fallback key, but save can be unlocked using the fallback key
  3845  
  3846  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  3847  
  3848  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  3849  	defer restore()
  3850  
  3851  	// setup a bootloader for setting the bootenv after we are done
  3852  	bloader := bootloadertest.Mock("mock", c.MkDir())
  3853  	bootloader.Force(bloader)
  3854  	defer bootloader.Force(nil)
  3855  
  3856  	restore = disks.MockMountPointDisksToPartitionMapping(
  3857  		map[disks.Mountpoint]*disks.MockDiskMapping{
  3858  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  3859  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  3860  			{
  3861  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  3862  				IsDecryptedDevice: true,
  3863  			}: defaultEncBootDisk,
  3864  		},
  3865  	)
  3866  	defer restore()
  3867  
  3868  	dataActivationAttempts := 0
  3869  	saveActivated := false
  3870  	unlockVolumeWithSealedKeyCalls := 0
  3871  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  3872  		unlockVolumeWithSealedKeyCalls++
  3873  		switch unlockVolumeWithSealedKeyCalls {
  3874  
  3875  		case 1:
  3876  			// ubuntu data can't be unlocked with run key
  3877  			c.Assert(name, Equals, "ubuntu-data")
  3878  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  3879  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3880  			c.Assert(err, IsNil)
  3881  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3882  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  3883  			c.Assert(opts.WhichModel, NotNil)
  3884  			dataActivationAttempts++
  3885  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with run object")
  3886  
  3887  		case 2:
  3888  			// nor can it be unlocked with fallback key
  3889  			c.Assert(name, Equals, "ubuntu-data")
  3890  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"))
  3891  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3892  			c.Assert(err, IsNil)
  3893  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  3894  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  3895  			c.Assert(opts.WhichModel, NotNil)
  3896  			dataActivationAttempts++
  3897  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with fallback object")
  3898  
  3899  		case 3:
  3900  			// we can however still unlock ubuntu-save (somehow?)
  3901  			c.Assert(name, Equals, "ubuntu-save")
  3902  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"))
  3903  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  3904  			c.Assert(err, IsNil)
  3905  			c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  3906  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  3907  			c.Assert(opts.WhichModel, NotNil)
  3908  			saveActivated = true
  3909  			return happyUnlocked("ubuntu-save", secboot.UnlockedWithSealedKey), nil
  3910  		default:
  3911  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  3912  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  3913  		}
  3914  	})
  3915  	defer restore()
  3916  
  3917  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  3918  
  3919  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  3920  		// nothing can call this function in the tested scenario
  3921  		c.Fatalf("unexpected call")
  3922  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  3923  	})
  3924  	defer restore()
  3925  
  3926  	measureEpochCalls := 0
  3927  	measureModelCalls := 0
  3928  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  3929  		measureEpochCalls++
  3930  		return nil
  3931  	})
  3932  	defer restore()
  3933  
  3934  	var measuredModel *asserts.Model
  3935  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  3936  		measureModelCalls++
  3937  		var err error
  3938  		measuredModel, err = findModel()
  3939  		if err != nil {
  3940  			return err
  3941  		}
  3942  		return nil
  3943  	})
  3944  	defer restore()
  3945  
  3946  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  3947  		ubuntuLabelMount("ubuntu-seed", "recover"),
  3948  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  3949  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  3950  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  3951  		{
  3952  			"tmpfs",
  3953  			boot.InitramfsDataDir,
  3954  			tmpfsMountOpts,
  3955  		},
  3956  		{
  3957  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3958  			boot.InitramfsUbuntuBootDir,
  3959  			needsFsckDiskMountOpts,
  3960  		},
  3961  		{
  3962  			"/dev/mapper/ubuntu-save-random",
  3963  			boot.InitramfsUbuntuSaveDir,
  3964  			nil,
  3965  		},
  3966  	}, nil)
  3967  	defer restore()
  3968  
  3969  	// ensure that we check that access to sealed keys were locked
  3970  	sealedKeysLocked := false
  3971  	restore = main.MockSecbootLockSealedKeys(func() error {
  3972  		sealedKeysLocked = true
  3973  		return nil
  3974  	})
  3975  	defer restore()
  3976  
  3977  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  3978  	c.Assert(err, IsNil)
  3979  
  3980  	// we always need to lock access to sealed keys
  3981  	c.Check(sealedKeysLocked, Equals, true)
  3982  
  3983  	modeEnv := filepath.Join(boot.InitramfsWritableDir, "var/lib/snapd/modeenv")
  3984  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  3985  recovery_system=20191118
  3986  base=core20_1.snap
  3987  model=my-brand/my-model
  3988  grade=signed
  3989  `)
  3990  
  3991  	checkDegradedJSON(c, map[string]interface{}{
  3992  		"ubuntu-boot": map[string]interface{}{
  3993  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  3994  			"mount-state":    "mounted",
  3995  			"find-state":     "found",
  3996  			"mount-location": boot.InitramfsUbuntuBootDir,
  3997  		},
  3998  		"ubuntu-data": map[string]interface{}{
  3999  			"find-state":   "found",
  4000  			"device":       "/dev/disk/by-partuuid/ubuntu-data-enc-partuuid",
  4001  			"unlock-state": "error-unlocking",
  4002  		},
  4003  		"ubuntu-save": map[string]interface{}{
  4004  			"device":         "/dev/mapper/ubuntu-save-random",
  4005  			"unlock-key":     "fallback",
  4006  			"unlock-state":   "unlocked",
  4007  			"mount-state":    "mounted",
  4008  			"find-state":     "found",
  4009  			"mount-location": boot.InitramfsUbuntuSaveDir,
  4010  		},
  4011  		"error-log": []interface{}{
  4012  			"cannot unlock encrypted ubuntu-data (device /dev/disk/by-partuuid/ubuntu-data-enc-partuuid) with sealed run key: failed to unlock ubuntu-data with run object",
  4013  			"cannot unlock encrypted ubuntu-data partition with sealed fallback key: failed to unlock ubuntu-data with fallback object",
  4014  		},
  4015  	})
  4016  
  4017  	bloader2, err := bootloader.Find("", nil)
  4018  	c.Assert(err, IsNil)
  4019  	m, err := bloader2.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  4020  	c.Assert(err, IsNil)
  4021  	c.Assert(m, DeepEquals, map[string]string{
  4022  		"snapd_recovery_system": "20191118",
  4023  		"snapd_recovery_mode":   "run",
  4024  	})
  4025  
  4026  	// since we didn't mount data at all, we won't have copied in files from
  4027  	// there and instead will copy safe defaults to the ephemeral data
  4028  	c.Assert(filepath.Join(boot.InitramfsRunMntDir, "/data/system-data/var/lib/console-conf/complete"), testutil.FilePresent)
  4029  
  4030  	c.Check(dataActivationAttempts, Equals, 2)
  4031  	c.Check(saveActivated, Equals, true)
  4032  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 3)
  4033  	c.Check(measureEpochCalls, Equals, 1)
  4034  	c.Check(measureModelCalls, Equals, 1)
  4035  	c.Check(measuredModel, DeepEquals, s.model)
  4036  
  4037  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  4038  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  4039  }
  4040  
  4041  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeDegradedAbsentDataUnencryptedSaveHappy(c *C) {
  4042  	// test a scenario when data cannot be found but unencrypted save can be
  4043  	// mounted
  4044  
  4045  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  4046  
  4047  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  4048  	defer restore()
  4049  
  4050  	// setup a bootloader for setting the bootenv after we are done
  4051  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4052  	bootloader.Force(bloader)
  4053  	defer bootloader.Force(nil)
  4054  
  4055  	// no ubuntu-data on the disk at all
  4056  	mockDiskNoData := &disks.MockDiskMapping{
  4057  		FilesystemLabelToPartUUID: map[string]string{
  4058  			"ubuntu-boot": "ubuntu-boot-partuuid",
  4059  			"ubuntu-seed": "ubuntu-seed-partuuid",
  4060  			"ubuntu-save": "ubuntu-save-partuuid",
  4061  		},
  4062  		DiskHasPartitions: true,
  4063  		DevNum:            "noDataUnenc",
  4064  	}
  4065  
  4066  	restore = disks.MockMountPointDisksToPartitionMapping(
  4067  		map[disks.Mountpoint]*disks.MockDiskMapping{
  4068  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: mockDiskNoData,
  4069  			{Mountpoint: boot.InitramfsUbuntuBootDir}: mockDiskNoData,
  4070  			{Mountpoint: boot.InitramfsUbuntuSaveDir}: mockDiskNoData,
  4071  		},
  4072  	)
  4073  	defer restore()
  4074  
  4075  	dataActivated := false
  4076  	unlockVolumeWithSealedKeyCalls := 0
  4077  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  4078  		unlockVolumeWithSealedKeyCalls++
  4079  		switch unlockVolumeWithSealedKeyCalls {
  4080  
  4081  		case 1:
  4082  			// ubuntu data can't be found at all
  4083  			c.Assert(name, Equals, "ubuntu-data")
  4084  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  4085  			_, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4086  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4087  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  4088  			c.Assert(opts.WhichModel, NotNil)
  4089  			// sanity check that we can't find a normal ubuntu-data either
  4090  			_, err = disk.FindMatchingPartitionUUIDWithFsLabel(name)
  4091  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4092  			dataActivated = true
  4093  			// data not found at all
  4094  			return notFoundPart(), fmt.Errorf("error enumerating to find ubuntu-data")
  4095  		default:
  4096  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  4097  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  4098  		}
  4099  	})
  4100  	defer restore()
  4101  
  4102  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  4103  
  4104  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  4105  		// nothing can call this function in the tested scenario
  4106  		c.Fatalf("unexpected call")
  4107  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  4108  	})
  4109  	defer restore()
  4110  
  4111  	measureEpochCalls := 0
  4112  	measureModelCalls := 0
  4113  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  4114  		measureEpochCalls++
  4115  		return nil
  4116  	})
  4117  	defer restore()
  4118  
  4119  	var measuredModel *asserts.Model
  4120  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  4121  		measureModelCalls++
  4122  		var err error
  4123  		measuredModel, err = findModel()
  4124  		if err != nil {
  4125  			return err
  4126  		}
  4127  		return nil
  4128  	})
  4129  	defer restore()
  4130  
  4131  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  4132  		ubuntuLabelMount("ubuntu-seed", "recover"),
  4133  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  4134  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  4135  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  4136  		{
  4137  			"tmpfs",
  4138  			boot.InitramfsDataDir,
  4139  			tmpfsMountOpts,
  4140  		},
  4141  		{
  4142  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4143  			boot.InitramfsUbuntuBootDir,
  4144  			needsFsckDiskMountOpts,
  4145  		},
  4146  		{
  4147  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  4148  			boot.InitramfsUbuntuSaveDir,
  4149  			nil,
  4150  		},
  4151  	}, nil)
  4152  	defer restore()
  4153  
  4154  	// ensure that we check that access to sealed keys were locked
  4155  	sealedKeysLocked := false
  4156  	restore = main.MockSecbootLockSealedKeys(func() error {
  4157  		sealedKeysLocked = true
  4158  		return nil
  4159  	})
  4160  	defer restore()
  4161  
  4162  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  4163  	c.Assert(err, IsNil)
  4164  
  4165  	// we always need to lock access to sealed keys
  4166  	c.Check(sealedKeysLocked, Equals, true)
  4167  
  4168  	modeEnv := filepath.Join(boot.InitramfsWritableDir, "var/lib/snapd/modeenv")
  4169  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  4170  recovery_system=20191118
  4171  base=core20_1.snap
  4172  model=my-brand/my-model
  4173  grade=signed
  4174  `)
  4175  
  4176  	checkDegradedJSON(c, map[string]interface{}{
  4177  		"ubuntu-boot": map[string]interface{}{
  4178  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4179  			"mount-state":    "mounted",
  4180  			"find-state":     "found",
  4181  			"mount-location": boot.InitramfsUbuntuBootDir,
  4182  		},
  4183  		"ubuntu-data": map[string]interface{}{
  4184  			"find-state": "not-found",
  4185  		},
  4186  		"ubuntu-save": map[string]interface{}{
  4187  			"device":         "/dev/disk/by-partuuid/ubuntu-save-partuuid",
  4188  			"mount-state":    "mounted",
  4189  			"find-state":     "found",
  4190  			"mount-location": boot.InitramfsUbuntuSaveDir,
  4191  		},
  4192  		"error-log": []interface{}{
  4193  			"cannot locate ubuntu-data partition for mounting host data: error enumerating to find ubuntu-data",
  4194  		},
  4195  	})
  4196  
  4197  	bloader2, err := bootloader.Find("", nil)
  4198  	c.Assert(err, IsNil)
  4199  	m, err := bloader2.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  4200  	c.Assert(err, IsNil)
  4201  	c.Assert(m, DeepEquals, map[string]string{
  4202  		"snapd_recovery_system": "20191118",
  4203  		"snapd_recovery_mode":   "run",
  4204  	})
  4205  
  4206  	// since we didn't mount data at all, we won't have copied in files from
  4207  	// there and instead will copy safe defaults to the ephemeral data
  4208  	c.Assert(filepath.Join(boot.InitramfsRunMntDir, "/data/system-data/var/lib/console-conf/complete"), testutil.FilePresent)
  4209  
  4210  	c.Check(dataActivated, Equals, true)
  4211  	// unlocked tried only once, when attempting to set up ubuntu-data
  4212  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 1)
  4213  	c.Check(measureEpochCalls, Equals, 1)
  4214  	c.Check(measureModelCalls, Equals, 1)
  4215  	c.Check(measuredModel, DeepEquals, s.model)
  4216  
  4217  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  4218  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  4219  }
  4220  
  4221  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeDegradedUnencryptedDataSaveEncryptedHappy(c *C) {
  4222  	// test a rather impossible scenario when data is unencrypted, but save
  4223  	// is encrypted and thus gets completely ignored, because plain data
  4224  	// implies plain save
  4225  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  4226  
  4227  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  4228  	defer restore()
  4229  
  4230  	// setup a bootloader for setting the bootenv after we are done
  4231  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4232  	bootloader.Force(bloader)
  4233  	defer bootloader.Force(nil)
  4234  
  4235  	// no ubuntu-data on the disk at all
  4236  	mockDiskDataUnencSaveEnc := &disks.MockDiskMapping{
  4237  		FilesystemLabelToPartUUID: map[string]string{
  4238  			"ubuntu-boot": "ubuntu-boot-partuuid",
  4239  			"ubuntu-seed": "ubuntu-seed-partuuid",
  4240  			// ubuntu-data is unencrypted but ubuntu-save is encrypted
  4241  			"ubuntu-data":     "ubuntu-data-partuuid",
  4242  			"ubuntu-save-enc": "ubuntu-save-enc-partuuid",
  4243  		},
  4244  		DiskHasPartitions: true,
  4245  		DevNum:            "dataUnencSaveEnc",
  4246  	}
  4247  
  4248  	restore = disks.MockMountPointDisksToPartitionMapping(
  4249  		map[disks.Mountpoint]*disks.MockDiskMapping{
  4250  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     mockDiskDataUnencSaveEnc,
  4251  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     mockDiskDataUnencSaveEnc,
  4252  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: mockDiskDataUnencSaveEnc,
  4253  			// we don't include the mountpoint for ubuntu-save, since it should
  4254  			// never be mounted
  4255  		},
  4256  	)
  4257  	defer restore()
  4258  
  4259  	dataActivated := false
  4260  	unlockVolumeWithSealedKeyCalls := 0
  4261  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  4262  		unlockVolumeWithSealedKeyCalls++
  4263  		switch unlockVolumeWithSealedKeyCalls {
  4264  
  4265  		case 1:
  4266  			// ubuntu data is a plain old unencrypted partition
  4267  			c.Assert(name, Equals, "ubuntu-data")
  4268  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  4269  			_, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4270  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4271  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  4272  			c.Assert(opts.WhichModel, NotNil)
  4273  			// sanity check that we can't find a normal ubuntu-data either
  4274  			partUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name)
  4275  			c.Assert(err, IsNil)
  4276  			c.Assert(partUUID, Equals, "ubuntu-data-partuuid")
  4277  			dataActivated = true
  4278  
  4279  			return foundUnencrypted("ubuntu-data"), nil
  4280  		default:
  4281  			// no other partition is activated via secboot calls
  4282  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  4283  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  4284  		}
  4285  	})
  4286  	defer restore()
  4287  
  4288  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  4289  
  4290  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  4291  		// nothing can call this function in the tested scenario
  4292  		c.Fatalf("unexpected call")
  4293  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  4294  	})
  4295  	defer restore()
  4296  
  4297  	measureEpochCalls := 0
  4298  	measureModelCalls := 0
  4299  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  4300  		measureEpochCalls++
  4301  		return nil
  4302  	})
  4303  	defer restore()
  4304  
  4305  	var measuredModel *asserts.Model
  4306  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  4307  		measureModelCalls++
  4308  		var err error
  4309  		measuredModel, err = findModel()
  4310  		if err != nil {
  4311  			return err
  4312  		}
  4313  		return nil
  4314  	})
  4315  	defer restore()
  4316  
  4317  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  4318  		ubuntuLabelMount("ubuntu-seed", "recover"),
  4319  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  4320  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  4321  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  4322  		{
  4323  			"tmpfs",
  4324  			boot.InitramfsDataDir,
  4325  			tmpfsMountOpts,
  4326  		},
  4327  		{
  4328  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4329  			boot.InitramfsUbuntuBootDir,
  4330  			needsFsckDiskMountOpts,
  4331  		},
  4332  		{
  4333  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  4334  			boot.InitramfsHostUbuntuDataDir,
  4335  			needsNoSuidDiskMountOpts,
  4336  		},
  4337  	}, nil)
  4338  	defer restore()
  4339  
  4340  	s.testRecoverModeHappy(c)
  4341  
  4342  	c.Check(dataActivated, Equals, true)
  4343  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 1)
  4344  	c.Check(measureEpochCalls, Equals, 1)
  4345  	c.Check(measureModelCalls, Equals, 1)
  4346  	c.Check(measuredModel, DeepEquals, s.model)
  4347  
  4348  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  4349  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  4350  
  4351  	// the system is not encrypted, even if encrypted save exists it gets
  4352  	// ignored
  4353  	c.Check(s.logs.String(), testutil.Contains, "ignoring unexpected encrypted ubuntu-save")
  4354  }
  4355  
  4356  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeDegradedEncryptedDataUnencryptedSaveHappy(c *C) {
  4357  	// test a scenario when data is encrypted, thus implying an encrypted
  4358  	// ubuntu save, but save found on the disk is unencrypted
  4359  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  4360  
  4361  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  4362  	defer restore()
  4363  
  4364  	// setup a bootloader for setting the bootenv after we are done
  4365  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4366  	bootloader.Force(bloader)
  4367  	defer bootloader.Force(nil)
  4368  
  4369  	mockDiskDataUnencSaveEnc := &disks.MockDiskMapping{
  4370  		FilesystemLabelToPartUUID: map[string]string{
  4371  			"ubuntu-boot":     "ubuntu-boot-partuuid",
  4372  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  4373  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
  4374  			// ubuntu-data is encrypted but ubuntu-save is not
  4375  			"ubuntu-save": "ubuntu-save-partuuid",
  4376  		},
  4377  		DiskHasPartitions: true,
  4378  		DevNum:            "dataUnencSaveEnc",
  4379  	}
  4380  
  4381  	restore = disks.MockMountPointDisksToPartitionMapping(
  4382  		map[disks.Mountpoint]*disks.MockDiskMapping{
  4383  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     mockDiskDataUnencSaveEnc,
  4384  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     mockDiskDataUnencSaveEnc,
  4385  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: mockDiskDataUnencSaveEnc,
  4386  			// we don't include the mountpoint for ubuntu-save, since it should
  4387  			// never be mounted - we fail as soon as we find the encrypted save
  4388  			// and unlock it, but before we mount it
  4389  		},
  4390  	)
  4391  	defer restore()
  4392  
  4393  	unlockVolumeWithSealedKeyCalls := 0
  4394  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  4395  		unlockVolumeWithSealedKeyCalls++
  4396  		switch unlockVolumeWithSealedKeyCalls {
  4397  
  4398  		case 1:
  4399  			// ubuntu-data is encrypted partition
  4400  			c.Assert(name, Equals, "ubuntu-data")
  4401  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  4402  			_, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4403  			c.Assert(err, IsNil)
  4404  			// sanity check that we can't find a normal ubuntu-data either
  4405  			_, err = disk.FindMatchingPartitionUUIDWithFsLabel(name)
  4406  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4407  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with run object")
  4408  		case 2:
  4409  			c.Assert(name, Equals, "ubuntu-data")
  4410  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"))
  4411  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with recovery object")
  4412  		case 3:
  4413  			// we are asked to unlock encrypted ubuntu-save with the recovery key
  4414  			c.Assert(name, Equals, "ubuntu-save")
  4415  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"))
  4416  			_, err := disk.FindMatchingPartitionUUIDWithFsLabel(name)
  4417  			c.Assert(err, IsNil)
  4418  			_, err = disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4419  			// sanity
  4420  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4421  			// but we find an unencrypted one instead
  4422  			return foundUnencrypted("ubuntu-save"), nil
  4423  		default:
  4424  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  4425  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  4426  		}
  4427  	})
  4428  	defer restore()
  4429  
  4430  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  4431  
  4432  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  4433  		// nothing can call this function in the tested scenario
  4434  		c.Fatalf("unexpected call")
  4435  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  4436  	})
  4437  	defer restore()
  4438  
  4439  	measureEpochCalls := 0
  4440  	measureModelCalls := 0
  4441  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  4442  		measureEpochCalls++
  4443  		return nil
  4444  	})
  4445  	defer restore()
  4446  
  4447  	var measuredModel *asserts.Model
  4448  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  4449  		measureModelCalls++
  4450  		var err error
  4451  		measuredModel, err = findModel()
  4452  		if err != nil {
  4453  			return err
  4454  		}
  4455  		return nil
  4456  	})
  4457  	defer restore()
  4458  
  4459  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  4460  		ubuntuLabelMount("ubuntu-seed", "recover"),
  4461  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  4462  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  4463  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  4464  		{
  4465  			"tmpfs",
  4466  			boot.InitramfsDataDir,
  4467  			tmpfsMountOpts,
  4468  		},
  4469  		{
  4470  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4471  			boot.InitramfsUbuntuBootDir,
  4472  			needsFsckDiskMountOpts,
  4473  		},
  4474  	}, nil)
  4475  	defer restore()
  4476  
  4477  	// ensure that we check that access to sealed keys were locked
  4478  	sealedKeysLocked := false
  4479  	restore = main.MockSecbootLockSealedKeys(func() error {
  4480  		sealedKeysLocked = true
  4481  		return nil
  4482  	})
  4483  	defer restore()
  4484  
  4485  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  4486  	c.Assert(err, ErrorMatches, `inconsistent disk encryption status: previous access resulted in encrypted, but now is unencrypted from partition ubuntu-save`)
  4487  
  4488  	// we always need to lock access to sealed keys
  4489  	c.Check(sealedKeysLocked, Equals, true)
  4490  
  4491  	// unlocking tried 3 times, first attempt tries to unlock ubuntu-data
  4492  	// with run key, then the recovery key, and lastly we tried to unlock
  4493  	// ubuntu-save with the recovery key
  4494  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 3)
  4495  	c.Check(measureEpochCalls, Equals, 1)
  4496  	c.Check(measureModelCalls, Equals, 1)
  4497  	c.Check(measuredModel, DeepEquals, s.model)
  4498  
  4499  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  4500  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  4501  }
  4502  
  4503  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeUnencryptedDataUnencryptedSaveHappy(c *C) {
  4504  	// test a scenario when data is unencrypted, same goes for save and the
  4505  	// test observes calls to secboot unlock helper
  4506  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  4507  
  4508  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  4509  	defer restore()
  4510  
  4511  	// setup a bootloader for setting the bootenv after we are done
  4512  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4513  	bootloader.Force(bloader)
  4514  	defer bootloader.Force(nil)
  4515  
  4516  	restore = disks.MockMountPointDisksToPartitionMapping(
  4517  		map[disks.Mountpoint]*disks.MockDiskMapping{
  4518  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  4519  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  4520  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  4521  			{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  4522  		},
  4523  	)
  4524  	defer restore()
  4525  
  4526  	unlockVolumeWithSealedKeyCalls := 0
  4527  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  4528  		unlockVolumeWithSealedKeyCalls++
  4529  		switch unlockVolumeWithSealedKeyCalls {
  4530  
  4531  		case 1:
  4532  			// ubuntu data is an unencrypted partition
  4533  			c.Assert(name, Equals, "ubuntu-data")
  4534  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  4535  			_, err := disk.FindMatchingPartitionUUIDWithFsLabel(name)
  4536  			c.Assert(err, IsNil)
  4537  			// sanity check that we can't find encrypted ubuntu-data
  4538  			_, err = disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4539  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4540  			return foundUnencrypted("ubuntu-data"), nil
  4541  		default:
  4542  			// we do not expect any more calls here, since
  4543  			// ubuntu-data was found unencrypted unlocking will not
  4544  			// be tried again
  4545  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  4546  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  4547  		}
  4548  	})
  4549  	defer restore()
  4550  
  4551  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  4552  
  4553  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  4554  		// nothing can call this function in the tested scenario
  4555  		c.Fatalf("unexpected call")
  4556  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  4557  	})
  4558  	defer restore()
  4559  
  4560  	measureEpochCalls := 0
  4561  	measureModelCalls := 0
  4562  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  4563  		measureEpochCalls++
  4564  		return nil
  4565  	})
  4566  	defer restore()
  4567  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  4568  		measureModelCalls++
  4569  		return nil
  4570  	})
  4571  	defer restore()
  4572  
  4573  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  4574  		ubuntuLabelMount("ubuntu-seed", "recover"),
  4575  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  4576  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  4577  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  4578  		{
  4579  			"tmpfs",
  4580  			boot.InitramfsDataDir,
  4581  			tmpfsMountOpts,
  4582  		},
  4583  		{
  4584  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4585  			boot.InitramfsUbuntuBootDir,
  4586  			needsFsckDiskMountOpts,
  4587  		},
  4588  		{
  4589  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  4590  			boot.InitramfsHostUbuntuDataDir,
  4591  			needsNoSuidDiskMountOpts,
  4592  		},
  4593  		{
  4594  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  4595  			boot.InitramfsUbuntuSaveDir,
  4596  			nil,
  4597  		},
  4598  	}, nil)
  4599  	defer restore()
  4600  
  4601  	s.testRecoverModeHappy(c)
  4602  
  4603  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 1)
  4604  	c.Check(measureEpochCalls, Equals, 1)
  4605  	c.Check(measureModelCalls, Equals, 1)
  4606  
  4607  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  4608  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  4609  }
  4610  
  4611  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedAbsentDataSaveUnlockFallbackHappy(c *C) {
  4612  	// test a scenario when data cannot be found but save can be
  4613  	// unlocked using the fallback key
  4614  
  4615  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  4616  
  4617  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  4618  	defer restore()
  4619  
  4620  	// setup a bootloader for setting the bootenv after we are done
  4621  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4622  	bootloader.Force(bloader)
  4623  	defer bootloader.Force(nil)
  4624  
  4625  	// no ubuntu-data on the disk at all
  4626  	mockDiskNoData := &disks.MockDiskMapping{
  4627  		FilesystemLabelToPartUUID: map[string]string{
  4628  			"ubuntu-boot":     "ubuntu-boot-partuuid",
  4629  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  4630  			"ubuntu-save-enc": "ubuntu-save-enc-partuuid",
  4631  		},
  4632  		DiskHasPartitions: true,
  4633  		DevNum:            "defaultEncDev",
  4634  	}
  4635  
  4636  	restore = disks.MockMountPointDisksToPartitionMapping(
  4637  		map[disks.Mountpoint]*disks.MockDiskMapping{
  4638  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: mockDiskNoData,
  4639  			{Mountpoint: boot.InitramfsUbuntuBootDir}: mockDiskNoData,
  4640  			{
  4641  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  4642  				IsDecryptedDevice: true,
  4643  			}: mockDiskNoData,
  4644  		},
  4645  	)
  4646  	defer restore()
  4647  
  4648  	dataActivated := false
  4649  	saveActivated := false
  4650  	unlockVolumeWithSealedKeyCalls := 0
  4651  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  4652  		unlockVolumeWithSealedKeyCalls++
  4653  		switch unlockVolumeWithSealedKeyCalls {
  4654  
  4655  		case 1:
  4656  			// ubuntu data can't be found at all
  4657  			c.Assert(name, Equals, "ubuntu-data")
  4658  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  4659  			_, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4660  			c.Assert(err, FitsTypeOf, disks.PartitionNotFoundError{})
  4661  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  4662  			c.Assert(opts.WhichModel, NotNil)
  4663  			dataActivated = true
  4664  			// data not found at all
  4665  			return notFoundPart(), fmt.Errorf("error enumerating to find ubuntu-data")
  4666  
  4667  		case 2:
  4668  			// we can however still unlock ubuntu-save with the fallback key
  4669  			c.Assert(name, Equals, "ubuntu-save")
  4670  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"))
  4671  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4672  			c.Assert(err, IsNil)
  4673  			c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  4674  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  4675  			c.Assert(opts.WhichModel, NotNil)
  4676  			saveActivated = true
  4677  			return happyUnlocked("ubuntu-save", secboot.UnlockedWithSealedKey), nil
  4678  		default:
  4679  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  4680  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  4681  		}
  4682  	})
  4683  	defer restore()
  4684  
  4685  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  4686  
  4687  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  4688  		// nothing can call this function in the tested scenario
  4689  		c.Fatalf("unexpected call")
  4690  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  4691  	})
  4692  	defer restore()
  4693  
  4694  	measureEpochCalls := 0
  4695  	measureModelCalls := 0
  4696  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  4697  		measureEpochCalls++
  4698  		return nil
  4699  	})
  4700  	defer restore()
  4701  
  4702  	var measuredModel *asserts.Model
  4703  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  4704  		measureModelCalls++
  4705  		var err error
  4706  		measuredModel, err = findModel()
  4707  		if err != nil {
  4708  			return err
  4709  		}
  4710  		return nil
  4711  	})
  4712  	defer restore()
  4713  
  4714  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  4715  		ubuntuLabelMount("ubuntu-seed", "recover"),
  4716  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  4717  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  4718  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  4719  		{
  4720  			"tmpfs",
  4721  			boot.InitramfsDataDir,
  4722  			tmpfsMountOpts,
  4723  		},
  4724  		{
  4725  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4726  			boot.InitramfsUbuntuBootDir,
  4727  			needsFsckDiskMountOpts,
  4728  		},
  4729  		{
  4730  			"/dev/mapper/ubuntu-save-random",
  4731  			boot.InitramfsUbuntuSaveDir,
  4732  			nil,
  4733  		},
  4734  	}, nil)
  4735  	defer restore()
  4736  
  4737  	// ensure that we check that access to sealed keys were locked
  4738  	sealedKeysLocked := false
  4739  	restore = main.MockSecbootLockSealedKeys(func() error {
  4740  		sealedKeysLocked = true
  4741  		return nil
  4742  	})
  4743  	defer restore()
  4744  
  4745  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  4746  	c.Assert(err, IsNil)
  4747  
  4748  	// we always need to lock access to sealed keys
  4749  	c.Check(sealedKeysLocked, Equals, true)
  4750  
  4751  	modeEnv := filepath.Join(boot.InitramfsWritableDir, "var/lib/snapd/modeenv")
  4752  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  4753  recovery_system=20191118
  4754  base=core20_1.snap
  4755  model=my-brand/my-model
  4756  grade=signed
  4757  `)
  4758  
  4759  	checkDegradedJSON(c, map[string]interface{}{
  4760  		"ubuntu-boot": map[string]interface{}{
  4761  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4762  			"mount-state":    "mounted",
  4763  			"find-state":     "found",
  4764  			"mount-location": boot.InitramfsUbuntuBootDir,
  4765  		},
  4766  		"ubuntu-data": map[string]interface{}{
  4767  			"find-state": "not-found",
  4768  		},
  4769  		"ubuntu-save": map[string]interface{}{
  4770  			"device":         "/dev/mapper/ubuntu-save-random",
  4771  			"unlock-key":     "fallback",
  4772  			"unlock-state":   "unlocked",
  4773  			"mount-state":    "mounted",
  4774  			"find-state":     "found",
  4775  			"mount-location": boot.InitramfsUbuntuSaveDir,
  4776  		},
  4777  		"error-log": []interface{}{
  4778  			"cannot locate ubuntu-data partition for mounting host data: error enumerating to find ubuntu-data",
  4779  		},
  4780  	})
  4781  
  4782  	bloader2, err := bootloader.Find("", nil)
  4783  	c.Assert(err, IsNil)
  4784  	m, err := bloader2.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  4785  	c.Assert(err, IsNil)
  4786  	c.Assert(m, DeepEquals, map[string]string{
  4787  		"snapd_recovery_system": "20191118",
  4788  		"snapd_recovery_mode":   "run",
  4789  	})
  4790  
  4791  	// since we didn't mount data at all, we won't have copied in files from
  4792  	// there and instead will copy safe defaults to the ephemeral data
  4793  	c.Assert(filepath.Join(boot.InitramfsRunMntDir, "/data/system-data/var/lib/console-conf/complete"), testutil.FilePresent)
  4794  
  4795  	c.Check(dataActivated, Equals, true)
  4796  	c.Check(saveActivated, Equals, true)
  4797  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 2)
  4798  	c.Check(measureEpochCalls, Equals, 1)
  4799  	c.Check(measureModelCalls, Equals, 1)
  4800  	c.Check(measuredModel, DeepEquals, s.model)
  4801  
  4802  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  4803  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  4804  }
  4805  
  4806  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedDegradedDataUnlockFailSaveUnlockFailHappy(c *C) {
  4807  	// test a scenario when unlocking data with both run and fallback keys
  4808  	// fails, followed by a failure to unlock save with the fallback key
  4809  
  4810  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  4811  
  4812  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  4813  	defer restore()
  4814  
  4815  	// setup a bootloader for setting the bootenv after we are done
  4816  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4817  	bootloader.Force(bloader)
  4818  	defer bootloader.Force(nil)
  4819  
  4820  	restore = disks.MockMountPointDisksToPartitionMapping(
  4821  		// no ubuntu-data mountpoint is mocked, but there is an
  4822  		// ubuntu-data-enc partition in the disk we find
  4823  		map[disks.Mountpoint]*disks.MockDiskMapping{
  4824  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  4825  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  4826  			{
  4827  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  4828  				IsDecryptedDevice: true,
  4829  			}: defaultEncBootDisk,
  4830  		},
  4831  	)
  4832  	defer restore()
  4833  
  4834  	dataActivationAttempts := 0
  4835  	saveUnsealActivationAttempted := false
  4836  	unlockVolumeWithSealedKeyCalls := 0
  4837  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  4838  		unlockVolumeWithSealedKeyCalls++
  4839  		switch unlockVolumeWithSealedKeyCalls {
  4840  
  4841  		case 1:
  4842  			// ubuntu data can't be unlocked with run key
  4843  			c.Assert(name, Equals, "ubuntu-data")
  4844  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  4845  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4846  			c.Assert(err, IsNil)
  4847  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  4848  			c.Assert(opts.AllowRecoveryKey, Equals, false)
  4849  			c.Assert(opts.WhichModel, NotNil)
  4850  			dataActivationAttempts++
  4851  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with run object")
  4852  
  4853  		case 2:
  4854  			// nor can it be unlocked with fallback key
  4855  			c.Assert(name, Equals, "ubuntu-data")
  4856  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key"))
  4857  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4858  			c.Assert(err, IsNil)
  4859  			c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  4860  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  4861  			c.Assert(opts.WhichModel, NotNil)
  4862  			dataActivationAttempts++
  4863  			return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with fallback object")
  4864  
  4865  		case 3:
  4866  			// we also fail to unlock save
  4867  
  4868  			// no attempts to activate ubuntu-save yet
  4869  			c.Assert(name, Equals, "ubuntu-save")
  4870  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key"))
  4871  			encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  4872  			c.Assert(err, IsNil)
  4873  			c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  4874  			c.Assert(opts.AllowRecoveryKey, Equals, true)
  4875  			c.Assert(opts.WhichModel, NotNil)
  4876  			saveUnsealActivationAttempted = true
  4877  			return foundEncrypted("ubuntu-save"), fmt.Errorf("failed to unlock ubuntu-save with fallback object")
  4878  
  4879  		default:
  4880  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  4881  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  4882  		}
  4883  	})
  4884  	defer restore()
  4885  
  4886  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "")
  4887  
  4888  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  4889  		// nothing can call this function in the tested scenario
  4890  		c.Fatalf("unexpected call")
  4891  		return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  4892  	})
  4893  	defer restore()
  4894  
  4895  	measureEpochCalls := 0
  4896  	measureModelCalls := 0
  4897  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  4898  		measureEpochCalls++
  4899  		return nil
  4900  	})
  4901  	defer restore()
  4902  
  4903  	var measuredModel *asserts.Model
  4904  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  4905  		measureModelCalls++
  4906  		var err error
  4907  		measuredModel, err = findModel()
  4908  		if err != nil {
  4909  			return err
  4910  		}
  4911  		return nil
  4912  	})
  4913  	defer restore()
  4914  
  4915  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  4916  		ubuntuLabelMount("ubuntu-seed", "recover"),
  4917  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  4918  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  4919  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  4920  		{
  4921  			"tmpfs",
  4922  			boot.InitramfsDataDir,
  4923  			tmpfsMountOpts,
  4924  		},
  4925  		{
  4926  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4927  			boot.InitramfsUbuntuBootDir,
  4928  			needsFsckDiskMountOpts,
  4929  		},
  4930  	}, nil)
  4931  	defer restore()
  4932  
  4933  	// ensure that we check that access to sealed keys were locked
  4934  	sealedKeysLocked := false
  4935  	restore = main.MockSecbootLockSealedKeys(func() error {
  4936  		sealedKeysLocked = true
  4937  		return nil
  4938  	})
  4939  	defer restore()
  4940  
  4941  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  4942  	c.Assert(err, IsNil)
  4943  
  4944  	// we always need to lock access to sealed keys
  4945  	c.Check(sealedKeysLocked, Equals, true)
  4946  
  4947  	modeEnv := filepath.Join(boot.InitramfsRunMntDir, "data/system-data/var/lib/snapd/modeenv")
  4948  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  4949  recovery_system=20191118
  4950  base=core20_1.snap
  4951  model=my-brand/my-model
  4952  grade=signed
  4953  `)
  4954  
  4955  	checkDegradedJSON(c, map[string]interface{}{
  4956  		"ubuntu-boot": map[string]interface{}{
  4957  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  4958  			"mount-state":    "mounted",
  4959  			"find-state":     "found",
  4960  			"mount-location": boot.InitramfsUbuntuBootDir,
  4961  		},
  4962  		"ubuntu-data": map[string]interface{}{
  4963  			"find-state":   "found",
  4964  			"device":       "/dev/disk/by-partuuid/ubuntu-data-enc-partuuid",
  4965  			"unlock-state": "error-unlocking",
  4966  		},
  4967  		"ubuntu-save": map[string]interface{}{
  4968  			"find-state":   "found",
  4969  			"device":       "/dev/disk/by-partuuid/ubuntu-save-enc-partuuid",
  4970  			"unlock-state": "error-unlocking",
  4971  		},
  4972  		"error-log": []interface{}{
  4973  			"cannot unlock encrypted ubuntu-data (device /dev/disk/by-partuuid/ubuntu-data-enc-partuuid) with sealed run key: failed to unlock ubuntu-data with run object",
  4974  			"cannot unlock encrypted ubuntu-data partition with sealed fallback key: failed to unlock ubuntu-data with fallback object",
  4975  			"cannot unlock encrypted ubuntu-save partition with sealed fallback key: failed to unlock ubuntu-save with fallback object",
  4976  		},
  4977  	})
  4978  
  4979  	bloader2, err := bootloader.Find("", nil)
  4980  	c.Assert(err, IsNil)
  4981  	m, err := bloader2.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  4982  	c.Assert(err, IsNil)
  4983  	c.Assert(m, DeepEquals, map[string]string{
  4984  		"snapd_recovery_system": "20191118",
  4985  		"snapd_recovery_mode":   "run",
  4986  	})
  4987  
  4988  	// since we didn't mount data at all, we won't have copied in files from
  4989  	// there and instead will copy safe defaults to the ephemeral data
  4990  	c.Assert(filepath.Join(boot.InitramfsRunMntDir, "/data/system-data/var/lib/console-conf/complete"), testutil.FilePresent)
  4991  
  4992  	c.Check(dataActivationAttempts, Equals, 2)
  4993  	c.Check(saveUnsealActivationAttempted, Equals, true)
  4994  	c.Check(unlockVolumeWithSealedKeyCalls, Equals, 3)
  4995  	c.Check(measureEpochCalls, Equals, 1)
  4996  	c.Check(measureModelCalls, Equals, 1)
  4997  	c.Check(measuredModel, DeepEquals, s.model)
  4998  
  4999  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  5000  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  5001  }
  5002  
  5003  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedMismatchedMarker(c *C) {
  5004  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  5005  
  5006  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  5007  	defer restore()
  5008  
  5009  	// setup a bootloader for setting the bootenv after we are done
  5010  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5011  	bootloader.Force(bloader)
  5012  	defer bootloader.Force(nil)
  5013  
  5014  	restore = disks.MockMountPointDisksToPartitionMapping(
  5015  		map[disks.Mountpoint]*disks.MockDiskMapping{
  5016  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  5017  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  5018  			{
  5019  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  5020  				IsDecryptedDevice: true,
  5021  			}: defaultEncBootDisk,
  5022  			{
  5023  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  5024  				IsDecryptedDevice: true,
  5025  			}: defaultEncBootDisk,
  5026  		},
  5027  	)
  5028  	defer restore()
  5029  
  5030  	dataActivated := false
  5031  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  5032  		c.Assert(name, Equals, "ubuntu-data")
  5033  		c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  5034  
  5035  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  5036  		c.Assert(err, IsNil)
  5037  		c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  5038  		c.Assert(opts.AllowRecoveryKey, Equals, false)
  5039  		c.Assert(opts.WhichModel, NotNil)
  5040  		dataActivated = true
  5041  		return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  5042  	})
  5043  	defer restore()
  5044  
  5045  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "other-marker")
  5046  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  5047  
  5048  	saveActivated := false
  5049  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  5050  		c.Check(dataActivated, Equals, true, Commentf("ubuntu-data not activated yet"))
  5051  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  5052  		c.Assert(err, IsNil)
  5053  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  5054  		c.Assert(key, DeepEquals, []byte("foo"))
  5055  		saveActivated = true
  5056  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  5057  	})
  5058  	defer restore()
  5059  
  5060  	measureEpochCalls := 0
  5061  	measureModelCalls := 0
  5062  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  5063  		measureEpochCalls++
  5064  		return nil
  5065  	})
  5066  	defer restore()
  5067  
  5068  	var measuredModel *asserts.Model
  5069  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  5070  		measureModelCalls++
  5071  		var err error
  5072  		measuredModel, err = findModel()
  5073  		if err != nil {
  5074  			return err
  5075  		}
  5076  		return nil
  5077  	})
  5078  	defer restore()
  5079  
  5080  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  5081  		ubuntuLabelMount("ubuntu-seed", "recover"),
  5082  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  5083  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  5084  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  5085  		{
  5086  			"tmpfs",
  5087  			boot.InitramfsDataDir,
  5088  			tmpfsMountOpts,
  5089  		},
  5090  		{
  5091  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  5092  			boot.InitramfsUbuntuBootDir,
  5093  			needsFsckDiskMountOpts,
  5094  		},
  5095  		{
  5096  			"/dev/mapper/ubuntu-data-random",
  5097  			boot.InitramfsHostUbuntuDataDir,
  5098  			needsNoSuidDiskMountOpts,
  5099  		},
  5100  		{
  5101  			"/dev/mapper/ubuntu-save-random",
  5102  			boot.InitramfsUbuntuSaveDir,
  5103  			nil,
  5104  		},
  5105  	}, nil)
  5106  	defer restore()
  5107  
  5108  	// ensure that we check that access to sealed keys were locked
  5109  	sealedKeysLocked := false
  5110  	restore = main.MockSecbootLockSealedKeys(func() error {
  5111  		sealedKeysLocked = true
  5112  		return nil
  5113  	})
  5114  	defer restore()
  5115  
  5116  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  5117  	c.Assert(err, IsNil)
  5118  
  5119  	// we always need to lock access to sealed keys
  5120  	c.Check(sealedKeysLocked, Equals, true)
  5121  
  5122  	modeEnv := filepath.Join(boot.InitramfsWritableDir, "var/lib/snapd/modeenv")
  5123  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  5124  recovery_system=20191118
  5125  base=core20_1.snap
  5126  model=my-brand/my-model
  5127  grade=signed
  5128  `)
  5129  
  5130  	checkDegradedJSON(c, map[string]interface{}{
  5131  		"ubuntu-boot": map[string]interface{}{
  5132  			"device":         "/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  5133  			"mount-state":    "mounted",
  5134  			"find-state":     "found",
  5135  			"mount-location": boot.InitramfsUbuntuBootDir,
  5136  		},
  5137  		"ubuntu-data": map[string]interface{}{
  5138  			"device":         "/dev/mapper/ubuntu-data-random",
  5139  			"unlock-state":   "unlocked",
  5140  			"find-state":     "found",
  5141  			"mount-state":    "mounted-untrusted",
  5142  			"unlock-key":     "run",
  5143  			"mount-location": boot.InitramfsHostUbuntuDataDir,
  5144  		},
  5145  		"ubuntu-save": map[string]interface{}{
  5146  			"device":         "/dev/mapper/ubuntu-save-random",
  5147  			"unlock-key":     "run",
  5148  			"unlock-state":   "unlocked",
  5149  			"mount-state":    "mounted",
  5150  			"find-state":     "found",
  5151  			"mount-location": boot.InitramfsUbuntuSaveDir,
  5152  		},
  5153  		"error-log": []interface{}{"cannot trust ubuntu-data, ubuntu-save and ubuntu-data are not marked as from the same install"},
  5154  	})
  5155  
  5156  	bloader2, err := bootloader.Find("", nil)
  5157  	c.Assert(err, IsNil)
  5158  	m, err := bloader2.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  5159  	c.Assert(err, IsNil)
  5160  	c.Assert(m, DeepEquals, map[string]string{
  5161  		"snapd_recovery_system": "20191118",
  5162  		"snapd_recovery_mode":   "run",
  5163  	})
  5164  
  5165  	// since we didn't mount data at all, we won't have copied in files from
  5166  	// there and instead will copy safe defaults to the ephemeral data
  5167  	c.Assert(filepath.Join(boot.InitramfsRunMntDir, "/data/system-data/var/lib/console-conf/complete"), testutil.FilePresent)
  5168  
  5169  	c.Check(dataActivated, Equals, true)
  5170  	c.Check(saveActivated, Equals, true)
  5171  	c.Check(measureEpochCalls, Equals, 1)
  5172  	c.Check(measureModelCalls, Equals, 1)
  5173  	c.Check(measuredModel, DeepEquals, s.model)
  5174  
  5175  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  5176  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  5177  }
  5178  
  5179  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedAttackerFSAttachedHappy(c *C) {
  5180  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  5181  
  5182  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  5183  	defer restore()
  5184  
  5185  	// setup a bootloader for setting the bootenv
  5186  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5187  	bootloader.Force(bloader)
  5188  	defer bootloader.Force(nil)
  5189  
  5190  	mockDisk := &disks.MockDiskMapping{
  5191  		FilesystemLabelToPartUUID: map[string]string{
  5192  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  5193  			"ubuntu-boot":     "ubuntu-boot-partuuid",
  5194  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
  5195  			"ubuntu-save-enc": "ubuntu-save-enc-partuuid",
  5196  		},
  5197  		DiskHasPartitions: true,
  5198  		DevNum:            "bootDev",
  5199  	}
  5200  	attackerDisk := &disks.MockDiskMapping{
  5201  		FilesystemLabelToPartUUID: map[string]string{
  5202  			"ubuntu-seed":     "ubuntu-seed-attacker-partuuid",
  5203  			"ubuntu-boot":     "ubuntu-boot-attacker-partuuid",
  5204  			"ubuntu-data-enc": "ubuntu-data-enc-attacker-partuuid",
  5205  			"ubuntu-save-enc": "ubuntu-save-enc-attacker-partuuid",
  5206  		},
  5207  		DiskHasPartitions: true,
  5208  		DevNum:            "attackerDev",
  5209  	}
  5210  
  5211  	restore = disks.MockMountPointDisksToPartitionMapping(
  5212  		map[disks.Mountpoint]*disks.MockDiskMapping{
  5213  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: mockDisk,
  5214  			{Mountpoint: boot.InitramfsUbuntuBootDir}: mockDisk,
  5215  			{
  5216  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  5217  				IsDecryptedDevice: true,
  5218  			}: mockDisk,
  5219  			{
  5220  				Mountpoint:        boot.InitramfsUbuntuSaveDir,
  5221  				IsDecryptedDevice: true,
  5222  			}: mockDisk,
  5223  			// this is the attacker fs on a different disk
  5224  			{Mountpoint: "somewhere-else"}: attackerDisk,
  5225  		},
  5226  	)
  5227  	defer restore()
  5228  
  5229  	activated := false
  5230  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  5231  		c.Assert(name, Equals, "ubuntu-data")
  5232  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  5233  		c.Assert(err, IsNil)
  5234  		c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  5235  		c.Assert(opts.AllowRecoveryKey, Equals, false)
  5236  		c.Assert(opts.WhichModel, NotNil)
  5237  		activated = true
  5238  		return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  5239  	})
  5240  	defer restore()
  5241  
  5242  	s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  5243  	s.mockUbuntuSaveMarker(c, boot.InitramfsUbuntuSaveDir, "marker")
  5244  
  5245  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  5246  		encDevPartUUID, err := disk.FindMatchingPartitionUUIDWithFsLabel(name + "-enc")
  5247  		c.Assert(err, IsNil)
  5248  		c.Assert(encDevPartUUID, Equals, "ubuntu-save-enc-partuuid")
  5249  		c.Assert(key, DeepEquals, []byte("foo"))
  5250  		return happyUnlocked("ubuntu-save", secboot.UnlockedWithKey), nil
  5251  	})
  5252  	defer restore()
  5253  
  5254  	measureEpochCalls := 0
  5255  	measureModelCalls := 0
  5256  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  5257  		measureEpochCalls++
  5258  		return nil
  5259  	})
  5260  	defer restore()
  5261  
  5262  	var measuredModel *asserts.Model
  5263  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  5264  		measureModelCalls++
  5265  		var err error
  5266  		measuredModel, err = findModel()
  5267  		if err != nil {
  5268  			return err
  5269  		}
  5270  		return nil
  5271  	})
  5272  	defer restore()
  5273  
  5274  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  5275  		ubuntuLabelMount("ubuntu-seed", "recover"),
  5276  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  5277  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  5278  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  5279  		{
  5280  			"tmpfs",
  5281  			boot.InitramfsDataDir,
  5282  			tmpfsMountOpts,
  5283  		},
  5284  		{
  5285  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  5286  			boot.InitramfsUbuntuBootDir,
  5287  			needsFsckDiskMountOpts,
  5288  		},
  5289  		{
  5290  			"/dev/mapper/ubuntu-data-random",
  5291  			boot.InitramfsHostUbuntuDataDir,
  5292  			needsNoSuidDiskMountOpts,
  5293  		},
  5294  		{
  5295  			"/dev/mapper/ubuntu-save-random",
  5296  			boot.InitramfsUbuntuSaveDir,
  5297  			nil,
  5298  		},
  5299  	}, nil)
  5300  	defer restore()
  5301  
  5302  	s.testRecoverModeHappy(c)
  5303  
  5304  	c.Check(activated, Equals, true)
  5305  	c.Check(measureEpochCalls, Equals, 1)
  5306  	c.Check(measureModelCalls, Equals, 1)
  5307  	c.Check(measuredModel, DeepEquals, s.model)
  5308  
  5309  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  5310  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  5311  }
  5312  
  5313  func (s *initramfsMountsSuite) testInitramfsMountsInstallRecoverModeMeasure(c *C, mode string) {
  5314  	s.mockProcCmdlineContent(c, fmt.Sprintf("snapd_recovery_mode=%s snapd_recovery_system=%s", mode, s.sysLabel))
  5315  
  5316  	modeMnts := []systemdMount{
  5317  		ubuntuLabelMount("ubuntu-seed", mode),
  5318  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  5319  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  5320  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  5321  		{
  5322  			"tmpfs",
  5323  			boot.InitramfsDataDir,
  5324  			tmpfsMountOpts,
  5325  		},
  5326  	}
  5327  
  5328  	mockDiskMapping := map[disks.Mountpoint]*disks.MockDiskMapping{
  5329  		{Mountpoint: boot.InitramfsUbuntuSeedDir}: {
  5330  			FilesystemLabelToPartUUID: map[string]string{
  5331  				"ubuntu-seed": "ubuntu-seed-partuuid",
  5332  			},
  5333  			DiskHasPartitions: true,
  5334  		},
  5335  	}
  5336  
  5337  	if mode == "recover" {
  5338  		// setup a bootloader for setting the bootenv after we are done
  5339  		bloader := bootloadertest.Mock("mock", c.MkDir())
  5340  		bootloader.Force(bloader)
  5341  		defer bootloader.Force(nil)
  5342  
  5343  		// add the expected mount of ubuntu-data onto the host data dir
  5344  		modeMnts = append(modeMnts,
  5345  			systemdMount{
  5346  				"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  5347  				boot.InitramfsUbuntuBootDir,
  5348  				needsFsckDiskMountOpts,
  5349  			},
  5350  			systemdMount{
  5351  				"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  5352  				boot.InitramfsHostUbuntuDataDir,
  5353  				needsNoSuidDiskMountOpts,
  5354  			},
  5355  			systemdMount{
  5356  				"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  5357  				boot.InitramfsUbuntuSaveDir,
  5358  				nil,
  5359  			})
  5360  
  5361  		// also add the ubuntu-data and ubuntu-save fs labels to the
  5362  		// disk referenced by the ubuntu-seed partition
  5363  		disk := mockDiskMapping[disks.Mountpoint{Mountpoint: boot.InitramfsUbuntuSeedDir}]
  5364  		disk.FilesystemLabelToPartUUID["ubuntu-boot"] = "ubuntu-boot-partuuid"
  5365  		disk.FilesystemLabelToPartUUID["ubuntu-data"] = "ubuntu-data-partuuid"
  5366  		disk.FilesystemLabelToPartUUID["ubuntu-save"] = "ubuntu-save-partuuid"
  5367  
  5368  		// and also add the /run/mnt/host/ubuntu-{boot,data,save} mountpoints
  5369  		// for cross-checking after mounting
  5370  		mockDiskMapping[disks.Mountpoint{Mountpoint: boot.InitramfsUbuntuBootDir}] = disk
  5371  		mockDiskMapping[disks.Mountpoint{Mountpoint: boot.InitramfsHostUbuntuDataDir}] = disk
  5372  		mockDiskMapping[disks.Mountpoint{Mountpoint: boot.InitramfsUbuntuSaveDir}] = disk
  5373  	}
  5374  
  5375  	restore := disks.MockMountPointDisksToPartitionMapping(mockDiskMapping)
  5376  	defer restore()
  5377  
  5378  	measureEpochCalls := 0
  5379  	measureModelCalls := 0
  5380  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  5381  		measureEpochCalls++
  5382  		return nil
  5383  	})
  5384  	defer restore()
  5385  
  5386  	var measuredModel *asserts.Model
  5387  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  5388  		measureModelCalls++
  5389  		var err error
  5390  		measuredModel, err = findModel()
  5391  		if err != nil {
  5392  			return err
  5393  		}
  5394  		return nil
  5395  	})
  5396  	defer restore()
  5397  
  5398  	restore = s.mockSystemdMountSequence(c, modeMnts, nil)
  5399  	defer restore()
  5400  
  5401  	if mode == "recover" {
  5402  		// use the helper
  5403  		s.testRecoverModeHappy(c)
  5404  	} else {
  5405  		_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  5406  		c.Assert(err, IsNil)
  5407  
  5408  		modeEnv := filepath.Join(boot.InitramfsDataDir, "/system-data/var/lib/snapd/modeenv")
  5409  		c.Check(modeEnv, testutil.FileEquals, `mode=install
  5410  recovery_system=20191118
  5411  base=core20_1.snap
  5412  model=my-brand/my-model
  5413  grade=signed
  5414  `)
  5415  	}
  5416  
  5417  	c.Check(measuredModel, NotNil)
  5418  	c.Check(measuredModel, DeepEquals, s.model)
  5419  	c.Check(measureEpochCalls, Equals, 1)
  5420  	c.Check(measureModelCalls, Equals, 1)
  5421  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  5422  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, s.sysLabel+"-model-measured"), testutil.FilePresent)
  5423  }
  5424  
  5425  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeMeasure(c *C) {
  5426  	s.testInitramfsMountsInstallRecoverModeMeasure(c, "install")
  5427  }
  5428  
  5429  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeUnsetMeasure(c *C) {
  5430  	// TODO:UC20: eventually we should require snapd_recovery_mode to be set to
  5431  	// explicitly "install" for install mode, but we originally allowed
  5432  	// snapd_recovery_mode="" and interpreted it as install mode, so test that
  5433  	// case too
  5434  	s.testInitramfsMountsInstallRecoverModeMeasure(c, "")
  5435  }
  5436  
  5437  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeMeasure(c *C) {
  5438  	s.testInitramfsMountsInstallRecoverModeMeasure(c, "recover")
  5439  }
  5440  
  5441  func (s *initramfsMountsSuite) runInitramfsMountsUnencryptedTryRecovery(c *C, triedSystem bool) (err error) {
  5442  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover  snapd_recovery_system="+s.sysLabel)
  5443  
  5444  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  5445  	defer restore()
  5446  	restore = disks.MockMountPointDisksToPartitionMapping(
  5447  		map[disks.Mountpoint]*disks.MockDiskMapping{
  5448  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootWithSaveDisk,
  5449  			{Mountpoint: boot.InitramfsUbuntuBootDir}:     defaultBootWithSaveDisk,
  5450  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootWithSaveDisk,
  5451  			{Mountpoint: boot.InitramfsUbuntuSaveDir}:     defaultBootWithSaveDisk,
  5452  		},
  5453  	)
  5454  	defer restore()
  5455  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  5456  		ubuntuLabelMount("ubuntu-seed", "recover"),
  5457  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  5458  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  5459  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  5460  		{
  5461  			"tmpfs",
  5462  			boot.InitramfsDataDir,
  5463  			tmpfsMountOpts,
  5464  		},
  5465  		{
  5466  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  5467  			boot.InitramfsUbuntuBootDir,
  5468  			needsFsckDiskMountOpts,
  5469  		},
  5470  		{
  5471  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  5472  			boot.InitramfsHostUbuntuDataDir,
  5473  			needsNoSuidDiskMountOpts,
  5474  		},
  5475  		{
  5476  			"/dev/disk/by-partuuid/ubuntu-save-partuuid",
  5477  			boot.InitramfsUbuntuSaveDir,
  5478  			nil,
  5479  		},
  5480  	}, nil)
  5481  	defer restore()
  5482  
  5483  	if triedSystem {
  5484  		defer func() {
  5485  			err = recover().(error)
  5486  		}()
  5487  	}
  5488  
  5489  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  5490  	return err
  5491  }
  5492  
  5493  func (s *initramfsMountsSuite) testInitramfsMountsTryRecoveryHappy(c *C, happyStatus string) {
  5494  	rebootCalls := 0
  5495  	restore := boot.MockInitramfsReboot(func() error {
  5496  		rebootCalls++
  5497  		return nil
  5498  	})
  5499  	defer restore()
  5500  
  5501  	bl := bootloadertest.Mock("bootloader", c.MkDir())
  5502  	bl.BootVars = map[string]string{
  5503  		"recovery_system_status": happyStatus,
  5504  		"try_recovery_system":    s.sysLabel,
  5505  	}
  5506  	bootloader.Force(bl)
  5507  	defer bootloader.Force(nil)
  5508  
  5509  	hostUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "host/ubuntu-data/")
  5510  	mockedState := filepath.Join(hostUbuntuData, "system-data/var/lib/snapd/state.json")
  5511  	c.Assert(os.MkdirAll(filepath.Dir(mockedState), 0750), IsNil)
  5512  	c.Assert(ioutil.WriteFile(mockedState, []byte(mockStateContent), 0640), IsNil)
  5513  
  5514  	const triedSystem = true
  5515  	err := s.runInitramfsMountsUnencryptedTryRecovery(c, triedSystem)
  5516  	// due to hackery with replacing reboot, we expect a non nil error that
  5517  	// actually indicates a success
  5518  	c.Assert(err, ErrorMatches, `finalize try recovery system did not reboot, last error: <nil>`)
  5519  
  5520  	// modeenv is not written as reboot happens before that
  5521  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
  5522  	c.Check(modeEnv, testutil.FileAbsent)
  5523  	c.Check(bl.BootVars, DeepEquals, map[string]string{
  5524  		"recovery_system_status": "tried",
  5525  		"try_recovery_system":    s.sysLabel,
  5526  		"snapd_recovery_mode":    "run",
  5527  		"snapd_recovery_system":  "",
  5528  	})
  5529  	c.Check(rebootCalls, Equals, 1)
  5530  }
  5531  
  5532  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryHappyTry(c *C) {
  5533  	s.testInitramfsMountsTryRecoveryHappy(c, "try")
  5534  }
  5535  
  5536  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryHappyTried(c *C) {
  5537  	s.testInitramfsMountsTryRecoveryHappy(c, "tried")
  5538  }
  5539  
  5540  func (s *initramfsMountsSuite) testInitramfsMountsTryRecoveryInconsistent(c *C) {
  5541  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover  snapd_recovery_system="+s.sysLabel)
  5542  
  5543  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  5544  	defer restore()
  5545  	restore = disks.MockMountPointDisksToPartitionMapping(
  5546  		map[disks.Mountpoint]*disks.MockDiskMapping{
  5547  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultBootWithSaveDisk,
  5548  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootWithSaveDisk,
  5549  		},
  5550  	)
  5551  	defer restore()
  5552  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  5553  		ubuntuLabelMount("ubuntu-seed", "recover"),
  5554  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  5555  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  5556  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  5557  		{
  5558  			"tmpfs",
  5559  			boot.InitramfsDataDir,
  5560  			tmpfsMountOpts,
  5561  		},
  5562  	}, nil)
  5563  	defer restore()
  5564  
  5565  	runParser := func() {
  5566  		main.Parser().ParseArgs([]string{"initramfs-mounts"})
  5567  	}
  5568  	c.Assert(runParser, PanicMatches, `finalize try recovery system did not reboot, last error: <nil>`)
  5569  }
  5570  
  5571  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryInconsistentBogusStatus(c *C) {
  5572  	rebootCalls := 0
  5573  	restore := boot.MockInitramfsReboot(func() error {
  5574  		rebootCalls++
  5575  		return nil
  5576  	})
  5577  	defer restore()
  5578  
  5579  	bl := bootloadertest.Mock("bootloader", c.MkDir())
  5580  	err := bl.SetBootVars(map[string]string{
  5581  		"recovery_system_status": "bogus",
  5582  		"try_recovery_system":    s.sysLabel,
  5583  	})
  5584  	c.Assert(err, IsNil)
  5585  	bootloader.Force(bl)
  5586  	defer bootloader.Force(nil)
  5587  
  5588  	s.testInitramfsMountsTryRecoveryInconsistent(c)
  5589  
  5590  	vars, err := bl.GetBootVars("recovery_system_status", "try_recovery_system",
  5591  		"snapd_recovery_mode", "snapd_recovery_system")
  5592  	c.Assert(err, IsNil)
  5593  	c.Check(vars, DeepEquals, map[string]string{
  5594  		"recovery_system_status": "",
  5595  		"try_recovery_system":    s.sysLabel,
  5596  		"snapd_recovery_mode":    "run",
  5597  		"snapd_recovery_system":  "",
  5598  	})
  5599  	c.Check(rebootCalls, Equals, 1)
  5600  	c.Check(s.logs.String(), testutil.Contains, `try recovery system state is inconsistent: unexpected recovery system status "bogus"`)
  5601  }
  5602  
  5603  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryInconsistentMissingLabel(c *C) {
  5604  	rebootCalls := 0
  5605  	restore := boot.MockInitramfsReboot(func() error {
  5606  		rebootCalls++
  5607  		return nil
  5608  	})
  5609  	defer restore()
  5610  
  5611  	bl := bootloadertest.Mock("bootloader", c.MkDir())
  5612  	err := bl.SetBootVars(map[string]string{
  5613  		"recovery_system_status": "try",
  5614  		"try_recovery_system":    "",
  5615  	})
  5616  	c.Assert(err, IsNil)
  5617  	bootloader.Force(bl)
  5618  	defer bootloader.Force(nil)
  5619  
  5620  	s.testInitramfsMountsTryRecoveryInconsistent(c)
  5621  
  5622  	vars, err := bl.GetBootVars("recovery_system_status", "try_recovery_system",
  5623  		"snapd_recovery_mode", "snapd_recovery_system")
  5624  	c.Assert(err, IsNil)
  5625  	c.Check(vars, DeepEquals, map[string]string{
  5626  		"recovery_system_status": "",
  5627  		"try_recovery_system":    "",
  5628  		"snapd_recovery_mode":    "run",
  5629  		"snapd_recovery_system":  "",
  5630  	})
  5631  	c.Check(rebootCalls, Equals, 1)
  5632  	c.Check(s.logs.String(), testutil.Contains, `try recovery system state is inconsistent: try recovery system is unset but status is "try"`)
  5633  }
  5634  
  5635  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryDifferentSystem(c *C) {
  5636  	rebootCalls := 0
  5637  	restore := boot.MockInitramfsReboot(func() error {
  5638  		rebootCalls++
  5639  		return nil
  5640  	})
  5641  	defer restore()
  5642  
  5643  	bl := bootloadertest.Mock("bootloader", c.MkDir())
  5644  	bl.BootVars = map[string]string{
  5645  		"recovery_system_status": "try",
  5646  		// a different system is expected to be tried
  5647  		"try_recovery_system": "1234",
  5648  	}
  5649  	bootloader.Force(bl)
  5650  	defer bootloader.Force(nil)
  5651  
  5652  	hostUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "host/ubuntu-data/")
  5653  	mockedState := filepath.Join(hostUbuntuData, "system-data/var/lib/snapd/state.json")
  5654  	c.Assert(os.MkdirAll(filepath.Dir(mockedState), 0750), IsNil)
  5655  	c.Assert(ioutil.WriteFile(mockedState, []byte(mockStateContent), 0640), IsNil)
  5656  
  5657  	const triedSystem = false
  5658  	err := s.runInitramfsMountsUnencryptedTryRecovery(c, triedSystem)
  5659  	c.Assert(err, IsNil)
  5660  
  5661  	// modeenv is written as we will seed the recovery system
  5662  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
  5663  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  5664  recovery_system=20191118
  5665  base=core20_1.snap
  5666  model=my-brand/my-model
  5667  grade=signed
  5668  `)
  5669  	c.Check(bl.BootVars, DeepEquals, map[string]string{
  5670  		// variables not modified since they were set up for a different
  5671  		// system
  5672  		"recovery_system_status": "try",
  5673  		"try_recovery_system":    "1234",
  5674  		// system is set up to go into run mode if rebooted
  5675  		"snapd_recovery_mode":   "run",
  5676  		"snapd_recovery_system": s.sysLabel,
  5677  	})
  5678  	// no reboot requests
  5679  	c.Check(rebootCalls, Equals, 0)
  5680  }
  5681  
  5682  func (s *initramfsMountsSuite) testInitramfsMountsTryRecoveryDegraded(c *C, expectedErr string, unlockDataFails, missingSaveKey bool) {
  5683  	// unlocking data and save failed, thus we consider this candidate
  5684  	// recovery system unusable
  5685  
  5686  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  5687  
  5688  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  5689  	defer restore()
  5690  
  5691  	bl := bootloadertest.Mock("bootloader", c.MkDir())
  5692  	bl.BootVars = map[string]string{
  5693  		"recovery_system_status": "try",
  5694  		"try_recovery_system":    s.sysLabel,
  5695  	}
  5696  	bootloader.Force(bl)
  5697  	defer bootloader.Force(nil)
  5698  
  5699  	mountMappings := map[disks.Mountpoint]*disks.MockDiskMapping{
  5700  		{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  5701  		{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  5702  		{
  5703  			Mountpoint:        boot.InitramfsUbuntuSaveDir,
  5704  			IsDecryptedDevice: true,
  5705  		}: defaultEncBootDisk,
  5706  	}
  5707  	mountSequence := []systemdMount{
  5708  		ubuntuLabelMount("ubuntu-seed", "recover"),
  5709  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  5710  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  5711  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  5712  		{
  5713  			"tmpfs",
  5714  			boot.InitramfsDataDir,
  5715  			tmpfsMountOpts,
  5716  		},
  5717  		{
  5718  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  5719  			boot.InitramfsUbuntuBootDir,
  5720  			needsFsckDiskMountOpts,
  5721  		},
  5722  	}
  5723  	if !unlockDataFails {
  5724  		// unlocking data is successful in this scenario
  5725  		mountMappings[disks.Mountpoint{
  5726  			Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  5727  			IsDecryptedDevice: true,
  5728  		}] = defaultEncBootDisk
  5729  		// and it got mounted too
  5730  		mountSequence = append(mountSequence, systemdMount{
  5731  			"/dev/mapper/ubuntu-data-random",
  5732  			boot.InitramfsHostUbuntuDataDir,
  5733  			needsNoSuidDiskMountOpts,
  5734  		})
  5735  	}
  5736  	if !missingSaveKey {
  5737  		s.mockUbuntuSaveKeyAndMarker(c, boot.InitramfsHostWritableDir, "foo", "marker")
  5738  	}
  5739  
  5740  	restore = disks.MockMountPointDisksToPartitionMapping(mountMappings)
  5741  	defer restore()
  5742  	unlockVolumeWithSealedKeyCalls := 0
  5743  	restore = main.MockSecbootUnlockVolumeUsingSealedKeyIfEncrypted(func(disk disks.Disk, name string, sealedEncryptionKeyFile string, opts *secboot.UnlockVolumeUsingSealedKeyOptions) (secboot.UnlockResult, error) {
  5744  		unlockVolumeWithSealedKeyCalls++
  5745  		switch unlockVolumeWithSealedKeyCalls {
  5746  
  5747  		case 1:
  5748  			// ubuntu data can't be unlocked with run key
  5749  			c.Assert(name, Equals, "ubuntu-data")
  5750  			c.Assert(sealedEncryptionKeyFile, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-boot/device/fde/ubuntu-data.sealed-key"))
  5751  			if unlockDataFails {
  5752  				// ubuntu-data can't be unlocked with the run key
  5753  				return foundEncrypted("ubuntu-data"), fmt.Errorf("failed to unlock ubuntu-data with run object")
  5754  			}
  5755  			return happyUnlocked("ubuntu-data", secboot.UnlockedWithSealedKey), nil
  5756  		default:
  5757  			c.Errorf("unexpected call to UnlockVolumeUsingSealedKeyIfEncrypted (num %d)", unlockVolumeWithSealedKeyCalls)
  5758  			return secboot.UnlockResult{}, fmt.Errorf("broken test")
  5759  		}
  5760  	})
  5761  	defer restore()
  5762  	unlockVolumeWithKeyCalls := 0
  5763  	restore = main.MockSecbootUnlockEncryptedVolumeUsingKey(func(disk disks.Disk, name string, key []byte) (secboot.UnlockResult, error) {
  5764  		unlockVolumeWithKeyCalls++
  5765  		switch unlockVolumeWithKeyCalls {
  5766  		case 1:
  5767  			if unlockDataFails {
  5768  				// unlocking data failed, with fallback disabled we should never reach here
  5769  				return secboot.UnlockResult{}, fmt.Errorf("unexpected call to unlock ubuntu-save, broken test")
  5770  			}
  5771  			// no attempts to activate ubuntu-save yet
  5772  			c.Assert(name, Equals, "ubuntu-save")
  5773  			c.Assert(key, DeepEquals, []byte("foo"))
  5774  			return foundEncrypted("ubuntu-save"), fmt.Errorf("failed to unlock ubuntu-save with key object")
  5775  		default:
  5776  			c.Fatalf("unexpected call")
  5777  			return secboot.UnlockResult{}, fmt.Errorf("unexpected call")
  5778  		}
  5779  	})
  5780  	defer restore()
  5781  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error { return nil })
  5782  	defer restore()
  5783  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  5784  		return nil
  5785  	})
  5786  	defer restore()
  5787  
  5788  	restore = s.mockSystemdMountSequence(c, mountSequence, nil)
  5789  	defer restore()
  5790  
  5791  	restore = main.MockSecbootLockSealedKeys(func() error {
  5792  		return nil
  5793  	})
  5794  	defer restore()
  5795  
  5796  	c.Assert(func() { main.Parser().ParseArgs([]string{"initramfs-mounts"}) }, PanicMatches,
  5797  		expectedErr)
  5798  
  5799  	modeEnv := filepath.Join(boot.InitramfsRunMntDir, "data/system-data/var/lib/snapd/modeenv")
  5800  	// modeenv is not written when trying out a recovery system
  5801  	c.Check(modeEnv, testutil.FileAbsent)
  5802  
  5803  	// degraded file is not written out as we always reboot
  5804  	c.Check(filepath.Join(dirs.SnapBootstrapRunDir, "degraded.json"), testutil.FileAbsent)
  5805  
  5806  	c.Check(bl.BootVars, DeepEquals, map[string]string{
  5807  		// variables not modified since the system is unsuccessful
  5808  		"recovery_system_status": "try",
  5809  		"try_recovery_system":    s.sysLabel,
  5810  		// system is set up to go into run more if rebooted
  5811  		"snapd_recovery_mode": "run",
  5812  		// recovery system is cleared
  5813  		"snapd_recovery_system": "",
  5814  	})
  5815  }
  5816  
  5817  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryDegradedStopAfterData(c *C) {
  5818  	rebootCalls := 0
  5819  	restore := boot.MockInitramfsReboot(func() error {
  5820  		rebootCalls++
  5821  		return nil
  5822  	})
  5823  	defer restore()
  5824  
  5825  	expectedErr := `finalize try recovery system did not reboot, last error: <nil>`
  5826  	const unlockDataFails = true
  5827  	const missingSaveKey = true
  5828  	s.testInitramfsMountsTryRecoveryDegraded(c, expectedErr, unlockDataFails, missingSaveKey)
  5829  
  5830  	// reboot was requested
  5831  	c.Check(rebootCalls, Equals, 1)
  5832  	c.Check(s.logs.String(), testutil.Contains, fmt.Sprintf(`try recovery system %q failed: cannot unlock ubuntu-data (fallback disabled)`, s.sysLabel))
  5833  }
  5834  
  5835  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryDegradedStopAfterSaveUnlockFailed(c *C) {
  5836  	rebootCalls := 0
  5837  	restore := boot.MockInitramfsReboot(func() error {
  5838  		rebootCalls++
  5839  		return nil
  5840  	})
  5841  	defer restore()
  5842  
  5843  	expectedErr := `finalize try recovery system did not reboot, last error: <nil>`
  5844  	const unlockDataFails = false
  5845  	const missingSaveKey = false
  5846  	s.testInitramfsMountsTryRecoveryDegraded(c, expectedErr, unlockDataFails, missingSaveKey)
  5847  
  5848  	// reboot was requested
  5849  	c.Check(rebootCalls, Equals, 1)
  5850  	c.Check(s.logs.String(), testutil.Contains, fmt.Sprintf(`try recovery system %q failed: cannot unlock ubuntu-save (fallback disabled)`, s.sysLabel))
  5851  }
  5852  
  5853  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryDegradedStopAfterSaveMissingKey(c *C) {
  5854  	rebootCalls := 0
  5855  	restore := boot.MockInitramfsReboot(func() error {
  5856  		rebootCalls++
  5857  		return nil
  5858  	})
  5859  	defer restore()
  5860  
  5861  	expectedErr := `finalize try recovery system did not reboot, last error: <nil>`
  5862  	const unlockDataFails = false
  5863  	const missingSaveKey = true
  5864  	s.testInitramfsMountsTryRecoveryDegraded(c, expectedErr, unlockDataFails, missingSaveKey)
  5865  
  5866  	// reboot was requested
  5867  	c.Check(rebootCalls, Equals, 1)
  5868  	c.Check(s.logs.String(), testutil.Contains, fmt.Sprintf(`try recovery system %q failed: cannot unlock ubuntu-save (fallback disabled)`, s.sysLabel))
  5869  }
  5870  
  5871  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryDegradedRebootFails(c *C) {
  5872  	rebootCalls := 0
  5873  	restore := boot.MockInitramfsReboot(func() error {
  5874  		rebootCalls++
  5875  		return fmt.Errorf("reboot fails")
  5876  	})
  5877  	defer restore()
  5878  
  5879  	expectedErr := `finalize try recovery system did not reboot, last error: cannot reboot to run system: reboot fails`
  5880  	const unlockDataFails = false
  5881  	const unlockSaveFails = false
  5882  	s.testInitramfsMountsTryRecoveryDegraded(c, expectedErr, unlockDataFails, unlockSaveFails)
  5883  
  5884  	// reboot was requested
  5885  	c.Check(rebootCalls, Equals, 1)
  5886  }
  5887  
  5888  func (s *initramfsMountsSuite) TestInitramfsMountsTryRecoveryHealthCheckFails(c *C) {
  5889  	rebootCalls := 0
  5890  	restore := boot.MockInitramfsReboot(func() error {
  5891  		rebootCalls++
  5892  		return nil
  5893  	})
  5894  	defer restore()
  5895  
  5896  	bl := bootloadertest.Mock("bootloader", c.MkDir())
  5897  	bl.BootVars = map[string]string{
  5898  		"recovery_system_status": "try",
  5899  		"try_recovery_system":    s.sysLabel,
  5900  	}
  5901  	bootloader.Force(bl)
  5902  	defer bootloader.Force(nil)
  5903  
  5904  	// prepare some state for the recovery process to reach a point where
  5905  	// the health check can be executed
  5906  	hostUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "host/ubuntu-data/")
  5907  	mockedState := filepath.Join(hostUbuntuData, "system-data/var/lib/snapd/state.json")
  5908  	c.Assert(os.MkdirAll(filepath.Dir(mockedState), 0750), IsNil)
  5909  	c.Assert(ioutil.WriteFile(mockedState, []byte(mockStateContent), 0640), IsNil)
  5910  
  5911  	restore = main.MockTryRecoverySystemHealthCheck(func() error {
  5912  		return fmt.Errorf("mock failure")
  5913  	})
  5914  	defer restore()
  5915  
  5916  	const triedSystem = true
  5917  	err := s.runInitramfsMountsUnencryptedTryRecovery(c, triedSystem)
  5918  	c.Assert(err, ErrorMatches, `finalize try recovery system did not reboot, last error: <nil>`)
  5919  
  5920  	modeEnv := filepath.Join(boot.InitramfsRunMntDir, "data/system-data/var/lib/snapd/modeenv")
  5921  	// modeenv is not written when trying out a recovery system
  5922  	c.Check(modeEnv, testutil.FileAbsent)
  5923  	c.Check(bl.BootVars, DeepEquals, map[string]string{
  5924  		// variables not modified since the health check failed
  5925  		"recovery_system_status": "try",
  5926  		"try_recovery_system":    s.sysLabel,
  5927  		// but system is set up to go back to run mode
  5928  		"snapd_recovery_mode":   "run",
  5929  		"snapd_recovery_system": "",
  5930  	})
  5931  	// reboot was requested
  5932  	c.Check(rebootCalls, Equals, 1)
  5933  	c.Check(s.logs.String(), testutil.Contains, `try recovery system health check failed: mock failure`)
  5934  }