github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/cmd/snap-bootstrap/cmd_initramfs_mounts_test.go (about)

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