github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"strings"
    29  	"time"
    30  
    31  	. "gopkg.in/check.v1"
    32  
    33  	"github.com/snapcore/snapd/asserts"
    34  	"github.com/snapcore/snapd/asserts/assertstest"
    35  	"github.com/snapcore/snapd/boot"
    36  	"github.com/snapcore/snapd/boot/boottest"
    37  	"github.com/snapcore/snapd/bootloader"
    38  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    39  	main "github.com/snapcore/snapd/cmd/snap-bootstrap"
    40  	"github.com/snapcore/snapd/dirs"
    41  	"github.com/snapcore/snapd/logger"
    42  	"github.com/snapcore/snapd/osutil/disks"
    43  	"github.com/snapcore/snapd/seed"
    44  	"github.com/snapcore/snapd/seed/seedtest"
    45  	"github.com/snapcore/snapd/snap"
    46  	"github.com/snapcore/snapd/systemd"
    47  	"github.com/snapcore/snapd/testutil"
    48  )
    49  
    50  var brandPrivKey, _ = assertstest.GenerateKey(752)
    51  
    52  type initramfsMountsSuite struct {
    53  	testutil.BaseTest
    54  
    55  	// makes available a bunch of helper (like MakeAssertedSnap)
    56  	*seedtest.TestingSeed20
    57  
    58  	Stdout *bytes.Buffer
    59  
    60  	seedDir  string
    61  	sysLabel string
    62  	model    *asserts.Model
    63  	tmpDir   string
    64  
    65  	kernel   snap.PlaceInfo
    66  	kernelr2 snap.PlaceInfo
    67  	core20   snap.PlaceInfo
    68  	core20r2 snap.PlaceInfo
    69  	snapd    snap.PlaceInfo
    70  }
    71  
    72  var _ = Suite(&initramfsMountsSuite{})
    73  
    74  var (
    75  	tmpfsMountOpts = &main.SystemdMountOptions{
    76  		Tmpfs: true,
    77  	}
    78  	needsFsckDiskMountOpts = &main.SystemdMountOptions{
    79  		NeedsFsck: true,
    80  	}
    81  
    82  	defaultBootDisk = &disks.MockDiskMapping{
    83  		FilesystemLabelToPartUUID: map[string]string{
    84  			// ubuntu-boot not strictly necessary, since we mount it first we
    85  			// don't go looking for the label ubuntu-boot on a disk, we just
    86  			// mount it and hope it's what we need, unless we have UEFI vars or
    87  			// something
    88  			"ubuntu-boot": "ubuntu-boot-partuuid",
    89  			"ubuntu-seed": "ubuntu-seed-partuuid",
    90  			"ubuntu-data": "ubuntu-data-partuuid",
    91  		},
    92  		DiskHasPartitions: true,
    93  		DevNum:            "default",
    94  	}
    95  
    96  	defaultEncBootDisk = &disks.MockDiskMapping{
    97  		FilesystemLabelToPartUUID: map[string]string{
    98  			// ubuntu-boot not strictly necessary, since we mount it first we
    99  			// don't ever search a particular disk for the ubuntu-boot label,
   100  			// we just mount it and hope it's what we need, unless we have UEFI
   101  			// vars or something a la boot.PartitionUUIDForBootedKernelDisk
   102  			"ubuntu-boot":     "ubuntu-boot-partuuid",
   103  			"ubuntu-seed":     "ubuntu-seed-partuuid",
   104  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
   105  		},
   106  		DiskHasPartitions: true,
   107  		DevNum:            "defaultEncDev",
   108  	}
   109  
   110  	mockStateContent = `{"data":{"auth":{"users":[{"id":1,"name":"mvo"}],"macaroon-key":"not-a-cookie","last-id":1}},"some":{"other":"stuff"}}`
   111  )
   112  
   113  // because 1.9 vet does not like xerrors.Errorf(".. %w")
   114  type mockedWrappedError struct {
   115  	err error
   116  	fmt string
   117  }
   118  
   119  func (m *mockedWrappedError) Unwrap() error { return m.err }
   120  
   121  func (m *mockedWrappedError) Error() string { return fmt.Sprintf(m.fmt, m.err) }
   122  
   123  func (s *initramfsMountsSuite) SetUpTest(c *C) {
   124  	s.BaseTest.SetUpTest(c)
   125  
   126  	s.Stdout = bytes.NewBuffer(nil)
   127  
   128  	_, restore := logger.MockLogger()
   129  	s.AddCleanup(restore)
   130  
   131  	s.tmpDir = c.MkDir()
   132  
   133  	// mock /run/mnt
   134  	dirs.SetRootDir(s.tmpDir)
   135  	restore = func() { dirs.SetRootDir("") }
   136  	s.AddCleanup(restore)
   137  
   138  	// pretend /run/mnt/ubuntu-seed has a valid seed
   139  	s.seedDir = boot.InitramfsUbuntuSeedDir
   140  
   141  	// now create a minimal uc20 seed dir with snaps/assertions
   142  	seed20 := &seedtest.TestingSeed20{SeedDir: s.seedDir}
   143  	seed20.SetupAssertSigning("canonical")
   144  	restore = seed.MockTrusted(seed20.StoreSigning.Trusted)
   145  	s.AddCleanup(restore)
   146  
   147  	// XXX: we don't really use this but seedtest always expects my-brand
   148  	seed20.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   149  		"verification": "verified",
   150  	})
   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", nil, 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  	s.sysLabel = "20191118"
   159  	s.model = seed20.MakeSeed(c, s.sysLabel, "my-brand", "my-model", map[string]interface{}{
   160  		"display-name": "my model",
   161  		"architecture": "amd64",
   162  		"base":         "core20",
   163  		"snaps": []interface{}{
   164  			map[string]interface{}{
   165  				"name":            "pc-kernel",
   166  				"id":              seed20.AssertedSnapID("pc-kernel"),
   167  				"type":            "kernel",
   168  				"default-channel": "20",
   169  			},
   170  			map[string]interface{}{
   171  				"name":            "pc",
   172  				"id":              seed20.AssertedSnapID("pc"),
   173  				"type":            "gadget",
   174  				"default-channel": "20",
   175  			}},
   176  	}, nil)
   177  
   178  	// make test snap PlaceInfo's for various boot functionality
   179  	var err error
   180  	s.kernel, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
   181  	c.Assert(err, IsNil)
   182  
   183  	s.core20, err = snap.ParsePlaceInfoFromSnapFileName("core20_1.snap")
   184  	c.Assert(err, IsNil)
   185  
   186  	s.kernelr2, err = snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap")
   187  	c.Assert(err, IsNil)
   188  
   189  	s.core20r2, err = snap.ParsePlaceInfoFromSnapFileName("core20_2.snap")
   190  	c.Assert(err, IsNil)
   191  
   192  	s.snapd, err = snap.ParsePlaceInfoFromSnapFileName("snapd_1.snap")
   193  	c.Assert(err, IsNil)
   194  
   195  	// by default mock that we don't have UEFI vars, etc. to get the booted
   196  	// kernel partition partition uuid
   197  	s.AddCleanup(main.MockPartitionUUIDForBootedKernelDisk(""))
   198  }
   199  
   200  // makeSnapFilesOnEarlyBootUbuntuData creates the snap files on ubuntu-data as
   201  // we
   202  func makeSnapFilesOnEarlyBootUbuntuData(c *C, snaps ...snap.PlaceInfo) {
   203  	snapDir := dirs.SnapBlobDirUnder(boot.InitramfsWritableDir)
   204  	err := os.MkdirAll(snapDir, 0755)
   205  	c.Assert(err, IsNil)
   206  	for _, sn := range snaps {
   207  		snFilename := sn.Filename()
   208  		err = ioutil.WriteFile(filepath.Join(snapDir, snFilename), nil, 0644)
   209  		c.Assert(err, IsNil)
   210  	}
   211  }
   212  
   213  func (s *initramfsMountsSuite) mockProcCmdlineContent(c *C, newContent string) {
   214  	mockProcCmdline := filepath.Join(c.MkDir(), "proc-cmdline")
   215  	err := ioutil.WriteFile(mockProcCmdline, []byte(newContent), 0644)
   216  	c.Assert(err, IsNil)
   217  	restore := boot.MockProcCmdline(mockProcCmdline)
   218  	s.AddCleanup(restore)
   219  }
   220  
   221  func (s *initramfsMountsSuite) TestInitramfsMountsNoModeError(c *C) {
   222  	s.mockProcCmdlineContent(c, "nothing-to-see")
   223  
   224  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   225  	c.Assert(err, ErrorMatches, "cannot detect mode nor recovery system to use")
   226  }
   227  
   228  func (s *initramfsMountsSuite) TestInitramfsMountsUnknownMode(c *C) {
   229  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install-foo")
   230  
   231  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   232  	c.Assert(err, ErrorMatches, `cannot use unknown mode "install-foo"`)
   233  }
   234  
   235  type systemdMount struct {
   236  	what  string
   237  	where string
   238  	opts  *main.SystemdMountOptions
   239  }
   240  
   241  // this is a function so we evaluate InitramfsUbuntuBootDir, etc at the time of
   242  // the test to pick up test-specific dirs.GlobalRootDir
   243  func ubuntuLabelMount(label string, mode string) systemdMount {
   244  	mnt := systemdMount{
   245  		opts: needsFsckDiskMountOpts,
   246  	}
   247  	switch label {
   248  	case "ubuntu-boot":
   249  		mnt.what = "/dev/disk/by-label/ubuntu-boot"
   250  		mnt.where = boot.InitramfsUbuntuBootDir
   251  	case "ubuntu-seed":
   252  		mnt.what = "/dev/disk/by-label/ubuntu-seed"
   253  		mnt.where = boot.InitramfsUbuntuSeedDir
   254  		// don't fsck in run mode
   255  		if mode == "run" {
   256  			mnt.opts = nil
   257  		}
   258  	case "ubuntu-data":
   259  		mnt.what = "/dev/disk/by-label/ubuntu-data"
   260  		mnt.where = boot.InitramfsDataDir
   261  	}
   262  
   263  	return mnt
   264  }
   265  
   266  // ubuntuPartUUIDMount returns a systemdMount for the partuuid disk, expecting
   267  // that the partuuid contains in it the expected label for easier coding
   268  func ubuntuPartUUIDMount(partuuid string, mode string) systemdMount {
   269  	mnt := systemdMount{
   270  		opts: needsFsckDiskMountOpts,
   271  	}
   272  	mnt.what = filepath.Join("/dev/disk/by-partuuid", partuuid)
   273  	switch {
   274  	case strings.Contains(partuuid, "ubuntu-boot"):
   275  		mnt.where = boot.InitramfsUbuntuBootDir
   276  	case strings.Contains(partuuid, "ubuntu-seed"):
   277  		mnt.where = boot.InitramfsUbuntuSeedDir
   278  		// don't fsck in run mode
   279  		if mode == "run" {
   280  			mnt.opts = nil
   281  		}
   282  	case strings.Contains(partuuid, "ubuntu-data"):
   283  		mnt.where = boot.InitramfsDataDir
   284  	}
   285  
   286  	return mnt
   287  }
   288  
   289  func (s *initramfsMountsSuite) makeSeedSnapSystemdMount(typ snap.Type) systemdMount {
   290  	mnt := systemdMount{}
   291  	var name, dir string
   292  	switch typ {
   293  	case snap.TypeSnapd:
   294  		name = "snapd"
   295  		dir = "snapd"
   296  	case snap.TypeBase:
   297  		name = "core20"
   298  		dir = "base"
   299  	case snap.TypeKernel:
   300  		name = "pc-kernel"
   301  		dir = "kernel"
   302  	}
   303  	mnt.what = filepath.Join(s.seedDir, "snaps", name+"_1.snap")
   304  	mnt.where = filepath.Join(boot.InitramfsRunMntDir, dir)
   305  
   306  	return mnt
   307  }
   308  
   309  func (s *initramfsMountsSuite) makeRunSnapSystemdMount(typ snap.Type, sn snap.PlaceInfo) systemdMount {
   310  	mnt := systemdMount{}
   311  	var dir string
   312  	switch typ {
   313  	case snap.TypeSnapd:
   314  		dir = "snapd"
   315  	case snap.TypeBase:
   316  		dir = "base"
   317  	case snap.TypeKernel:
   318  		dir = "kernel"
   319  	}
   320  
   321  	mnt.what = filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), sn.Filename())
   322  	mnt.where = filepath.Join(boot.InitramfsRunMntDir, dir)
   323  
   324  	return mnt
   325  }
   326  
   327  func (s *initramfsMountsSuite) mockSystemdMountSequence(c *C, mounts []systemdMount, comment CommentInterface) (restore func()) {
   328  	n := 0
   329  	if comment == nil {
   330  		comment = Commentf("")
   331  	}
   332  	s.AddCleanup(func() {
   333  		// make sure that after the test is done, we had as many mount calls as
   334  		// mocked mounts
   335  		c.Assert(n, Equals, len(mounts), comment)
   336  	})
   337  	return main.MockSystemdMount(func(what, where string, opts *main.SystemdMountOptions) error {
   338  		n++
   339  		c.Assert(n <= len(mounts), Equals, true)
   340  		if n > len(mounts) {
   341  			return fmt.Errorf("unexpected systemd-mount call: %s, %s, %+v", what, where, opts)
   342  		}
   343  		mnt := mounts[n-1]
   344  		c.Assert(what, Equals, mnt.what, comment)
   345  		c.Assert(where, Equals, mnt.where, comment)
   346  		c.Assert(opts, DeepEquals, mnt.opts, comment)
   347  		return nil
   348  	})
   349  }
   350  
   351  // TODO:UC20: write version that actually calls a mocked systemd-mount and
   352  //            ensures the right symlinks are there, etc.
   353  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeHappy(c *C) {
   354  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   355  
   356  	restore := s.mockSystemdMountSequence(c, []systemdMount{
   357  		ubuntuLabelMount("ubuntu-seed", "install"),
   358  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   359  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
   360  		s.makeSeedSnapSystemdMount(snap.TypeBase),
   361  		{
   362  			"tmpfs",
   363  			boot.InitramfsDataDir,
   364  			tmpfsMountOpts,
   365  		},
   366  	}, nil)
   367  	defer restore()
   368  
   369  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   370  	c.Assert(err, IsNil)
   371  
   372  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
   373  	c.Check(modeEnv, testutil.FileEquals, `mode=install
   374  recovery_system=20191118
   375  `)
   376  	cloudInitDisable := filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/cloud/cloud-init.disabled")
   377  	c.Check(cloudInitDisable, testutil.FilePresent)
   378  }
   379  
   380  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeBootedKernelPartitionUUIDHappy(c *C) {
   381  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   382  
   383  	restore := main.MockPartitionUUIDForBootedKernelDisk("specific-ubuntu-seed-partuuid")
   384  	defer restore()
   385  
   386  	restore = s.mockSystemdMountSequence(c, []systemdMount{
   387  		{
   388  			"/dev/disk/by-partuuid/specific-ubuntu-seed-partuuid",
   389  			boot.InitramfsUbuntuSeedDir,
   390  			needsFsckDiskMountOpts,
   391  		},
   392  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   393  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
   394  		s.makeSeedSnapSystemdMount(snap.TypeBase),
   395  		{
   396  			"tmpfs",
   397  			boot.InitramfsDataDir,
   398  			tmpfsMountOpts,
   399  		},
   400  	}, nil)
   401  	defer restore()
   402  
   403  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   404  	c.Assert(err, IsNil)
   405  
   406  	modeEnv := dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)
   407  	c.Check(modeEnv, testutil.FileEquals, `mode=install
   408  recovery_system=20191118
   409  `)
   410  	cloudInitDisable := filepath.Join(boot.InitramfsWritableDir, "_writable_defaults/etc/cloud/cloud-init.disabled")
   411  	c.Check(cloudInitDisable, testutil.FilePresent)
   412  }
   413  
   414  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeHappy(c *C) {
   415  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   416  
   417  	restore := disks.MockMountPointDisksToPartitionMapping(
   418  		map[disks.Mountpoint]*disks.MockDiskMapping{
   419  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
   420  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
   421  		},
   422  	)
   423  	defer restore()
   424  
   425  	restore = s.mockSystemdMountSequence(c, []systemdMount{
   426  		ubuntuLabelMount("ubuntu-boot", "run"),
   427  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
   428  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
   429  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
   430  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
   431  	}, nil)
   432  	defer restore()
   433  
   434  	// mock a bootloader
   435  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   436  	bootloader.Force(bloader)
   437  	defer bootloader.Force(nil)
   438  
   439  	// set the current kernel
   440  	restore = bloader.SetEnabledKernel(s.kernel)
   441  	defer restore()
   442  
   443  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   444  
   445  	// write modeenv
   446  	modeEnv := boot.Modeenv{
   447  		Mode:           "run",
   448  		Base:           s.core20.Filename(),
   449  		CurrentKernels: []string{s.kernel.Filename()},
   450  	}
   451  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   452  	c.Assert(err, IsNil)
   453  
   454  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   455  	c.Assert(err, IsNil)
   456  }
   457  
   458  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeRealSystemdMountTimesOutNoMount(c *C) {
   459  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   460  
   461  	testStart := time.Now()
   462  	timeCalls := 0
   463  	restore := main.MockTimeNow(func() time.Time {
   464  		timeCalls++
   465  		switch timeCalls {
   466  		case 1, 2:
   467  			return testStart
   468  		case 3:
   469  			// 1:31 later, we should time out
   470  			return testStart.Add(1*time.Minute + 31*time.Second)
   471  		default:
   472  			c.Errorf("unexpected time.Now() call (%d)", timeCalls)
   473  			// we want the test to fail at some point and not run forever, so
   474  			// move time way forward to make it for sure time out
   475  			return testStart.Add(10000 * time.Hour)
   476  		}
   477  	})
   478  	defer restore()
   479  
   480  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
   481  	defer cmd.Restore()
   482  
   483  	isMountedCalls := 0
   484  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
   485  		isMountedCalls++
   486  		switch isMountedCalls {
   487  		// always return false for the mount
   488  		case 1, 2:
   489  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
   490  			return false, nil
   491  		default:
   492  			// shouldn't be called more than twice due to the time.Now() mocking
   493  			c.Errorf("test broken, IsMounted called too many (%d) times", isMountedCalls)
   494  			return false, fmt.Errorf("test broken, IsMounted called too many (%d) times", isMountedCalls)
   495  		}
   496  	})
   497  	defer restore()
   498  
   499  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
   500  	c.Assert(err, ErrorMatches, fmt.Sprintf("timed out after 1m30s waiting for mount %s on %s", "/dev/disk/by-label/ubuntu-seed", boot.InitramfsUbuntuSeedDir))
   501  	c.Check(s.Stdout.String(), Equals, "")
   502  
   503  }
   504  
   505  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeHappyRealSystemdMount(c *C) {
   506  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=install snapd_recovery_system="+s.sysLabel)
   507  
   508  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
   509  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
   510  	snapdMnt := filepath.Join(boot.InitramfsRunMntDir, "snapd")
   511  
   512  	// don't do anything from systemd-mount, we verify the arguments passed at
   513  	// the end with cmd.Calls
   514  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
   515  	defer cmd.Restore()
   516  
   517  	// mock that in turn, /run/mnt/ubuntu-boot, /run/mnt/ubuntu-seed, etc. are
   518  	// mounted
   519  	n := 0
   520  	restore := main.MockOsutilIsMounted(func(where string) (bool, error) {
   521  		n++
   522  		switch n {
   523  		// first call for each mount returns false, then returns true, this
   524  		// tests in the case where systemd is racy / inconsistent and things
   525  		// aren't mounted by the time systemd-mount returns
   526  		case 1, 2:
   527  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
   528  			return n%2 == 0, nil
   529  		case 3, 4:
   530  			c.Assert(where, Equals, snapdMnt)
   531  			return n%2 == 0, nil
   532  		case 5, 6:
   533  			c.Assert(where, Equals, kernelMnt)
   534  			return n%2 == 0, nil
   535  		case 7, 8:
   536  			c.Assert(where, Equals, baseMnt)
   537  			return n%2 == 0, nil
   538  		case 9, 10:
   539  			c.Assert(where, Equals, boot.InitramfsDataDir)
   540  			return n%2 == 0, nil
   541  		default:
   542  			c.Errorf("unexpected IsMounted check on %s", where)
   543  			return false, fmt.Errorf("unexpected IsMounted check on %s", where)
   544  		}
   545  	})
   546  	defer restore()
   547  
   548  	// mock a bootloader
   549  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   550  	bootloader.Force(bloader)
   551  	defer bootloader.Force(nil)
   552  
   553  	// set the current kernel
   554  	restore = bloader.SetEnabledKernel(s.kernel)
   555  	defer restore()
   556  
   557  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   558  
   559  	// write modeenv
   560  	modeEnv := boot.Modeenv{
   561  		Mode:           "run",
   562  		Base:           s.core20.Filename(),
   563  		CurrentKernels: []string{s.kernel.Filename()},
   564  	}
   565  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   566  	c.Assert(err, IsNil)
   567  
   568  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   569  	c.Assert(err, IsNil)
   570  	c.Check(s.Stdout.String(), Equals, "")
   571  
   572  	// check that all of the override files are present
   573  	for _, initrdUnit := range []string{
   574  		"initrd.target",
   575  		"initrd-fs.target",
   576  		"initrd-switch-root.target",
   577  		"local-fs.target",
   578  	} {
   579  		for _, mountUnit := range []string{
   580  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSeedDir),
   581  			systemd.EscapeUnitNamePath(snapdMnt),
   582  			systemd.EscapeUnitNamePath(kernelMnt),
   583  			systemd.EscapeUnitNamePath(baseMnt),
   584  			systemd.EscapeUnitNamePath(boot.InitramfsDataDir),
   585  		} {
   586  			fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
   587  			unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
   588  			c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
   589  Requires=%[1]s
   590  After=%[1]s
   591  `, mountUnit+".mount"))
   592  		}
   593  	}
   594  
   595  	// 2 IsMounted calls per mount point, so 10 total IsMounted calls
   596  	c.Assert(n, Equals, 10)
   597  
   598  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
   599  		{
   600  			"systemd-mount",
   601  			"/dev/disk/by-label/ubuntu-seed",
   602  			boot.InitramfsUbuntuSeedDir,
   603  			"--no-pager",
   604  			"--no-ask-password",
   605  			"--fsck=yes",
   606  		},
   607  		{
   608  			"systemd-mount",
   609  			filepath.Join(s.seedDir, "snaps", s.snapd.Filename()),
   610  			snapdMnt,
   611  			"--no-pager",
   612  			"--no-ask-password",
   613  			"--fsck=no",
   614  		},
   615  		{
   616  			"systemd-mount",
   617  			filepath.Join(s.seedDir, "snaps", s.kernel.Filename()),
   618  			kernelMnt,
   619  			"--no-pager",
   620  			"--no-ask-password",
   621  			"--fsck=no",
   622  		},
   623  		{
   624  			"systemd-mount",
   625  			filepath.Join(s.seedDir, "snaps", s.core20.Filename()),
   626  			baseMnt,
   627  			"--no-pager",
   628  			"--no-ask-password",
   629  			"--fsck=no",
   630  		},
   631  		{
   632  			"systemd-mount",
   633  			"tmpfs",
   634  			boot.InitramfsDataDir,
   635  			"--no-pager",
   636  			"--no-ask-password",
   637  			"--type=tmpfs",
   638  			"--fsck=no",
   639  		},
   640  	})
   641  }
   642  
   643  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappyRealSystemdMount(c *C) {
   644  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
   645  
   646  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
   647  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
   648  	snapdMnt := filepath.Join(boot.InitramfsRunMntDir, "snapd")
   649  
   650  	restore := disks.MockMountPointDisksToPartitionMapping(
   651  		map[disks.Mountpoint]*disks.MockDiskMapping{
   652  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootDisk,
   653  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootDisk,
   654  		},
   655  	)
   656  	defer restore()
   657  
   658  	// don't do anything from systemd-mount, we verify the arguments passed at
   659  	// the end with cmd.Calls
   660  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
   661  	defer cmd.Restore()
   662  
   663  	// mock that in turn, /run/mnt/ubuntu-boot, /run/mnt/ubuntu-seed, etc. are
   664  	// mounted
   665  	n := 0
   666  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
   667  		n++
   668  		switch n {
   669  		// first call for each mount returns false, then returns true, this
   670  		// tests in the case where systemd is racy / inconsistent and things
   671  		// aren't mounted by the time systemd-mount returns
   672  		case 1, 2:
   673  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
   674  			return n%2 == 0, nil
   675  		case 3, 4:
   676  			c.Assert(where, Equals, snapdMnt)
   677  			return n%2 == 0, nil
   678  		case 5, 6:
   679  			c.Assert(where, Equals, kernelMnt)
   680  			return n%2 == 0, nil
   681  		case 7, 8:
   682  			c.Assert(where, Equals, baseMnt)
   683  			return n%2 == 0, nil
   684  		case 9, 10:
   685  			c.Assert(where, Equals, boot.InitramfsDataDir)
   686  			return n%2 == 0, nil
   687  		case 11, 12:
   688  			c.Assert(where, Equals, boot.InitramfsHostUbuntuDataDir)
   689  			return n%2 == 0, nil
   690  		default:
   691  			c.Errorf("unexpected IsMounted check on %s", where)
   692  			return false, fmt.Errorf("unexpected IsMounted check on %s", where)
   693  		}
   694  	})
   695  	defer restore()
   696  
   697  	// mock a bootloader
   698  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   699  	bootloader.Force(bloader)
   700  	defer bootloader.Force(nil)
   701  
   702  	// set the current kernel
   703  	restore = bloader.SetEnabledKernel(s.kernel)
   704  	defer restore()
   705  
   706  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   707  
   708  	// write modeenv
   709  	modeEnv := boot.Modeenv{
   710  		Mode:           "run",
   711  		Base:           s.core20.Filename(),
   712  		CurrentKernels: []string{s.kernel.Filename()},
   713  	}
   714  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   715  	c.Assert(err, IsNil)
   716  
   717  	s.testRecoverModeHappy(c)
   718  
   719  	c.Check(s.Stdout.String(), Equals, "")
   720  
   721  	// check that all of the override files are present
   722  	for _, initrdUnit := range []string{
   723  		"initrd.target",
   724  		"initrd-fs.target",
   725  		"initrd-switch-root.target",
   726  		"local-fs.target",
   727  	} {
   728  		for _, mountUnit := range []string{
   729  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSeedDir),
   730  			systemd.EscapeUnitNamePath(snapdMnt),
   731  			systemd.EscapeUnitNamePath(kernelMnt),
   732  			systemd.EscapeUnitNamePath(baseMnt),
   733  			systemd.EscapeUnitNamePath(boot.InitramfsDataDir),
   734  			systemd.EscapeUnitNamePath(boot.InitramfsHostUbuntuDataDir),
   735  		} {
   736  			fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
   737  			unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
   738  			c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
   739  Requires=%[1]s
   740  After=%[1]s
   741  `, mountUnit+".mount"))
   742  		}
   743  	}
   744  
   745  	// 2 IsMounted calls per mount point, so 12 total IsMounted calls
   746  	c.Assert(n, Equals, 12)
   747  
   748  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
   749  		{
   750  			"systemd-mount",
   751  			"/dev/disk/by-label/ubuntu-seed",
   752  			boot.InitramfsUbuntuSeedDir,
   753  			"--no-pager",
   754  			"--no-ask-password",
   755  			"--fsck=yes",
   756  		},
   757  		{
   758  			"systemd-mount",
   759  			filepath.Join(s.seedDir, "snaps", s.snapd.Filename()),
   760  			snapdMnt,
   761  			"--no-pager",
   762  			"--no-ask-password",
   763  			"--fsck=no",
   764  		},
   765  		{
   766  			"systemd-mount",
   767  			filepath.Join(s.seedDir, "snaps", s.kernel.Filename()),
   768  			kernelMnt,
   769  			"--no-pager",
   770  			"--no-ask-password",
   771  			"--fsck=no",
   772  		},
   773  		{
   774  			"systemd-mount",
   775  			filepath.Join(s.seedDir, "snaps", s.core20.Filename()),
   776  			baseMnt,
   777  			"--no-pager",
   778  			"--no-ask-password",
   779  			"--fsck=no",
   780  		},
   781  		{
   782  			"systemd-mount",
   783  			"tmpfs",
   784  			boot.InitramfsDataDir,
   785  			"--no-pager",
   786  			"--no-ask-password",
   787  			"--type=tmpfs",
   788  			"--fsck=no",
   789  		},
   790  		{
   791  			"systemd-mount",
   792  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
   793  			boot.InitramfsHostUbuntuDataDir,
   794  			"--no-pager",
   795  			"--no-ask-password",
   796  			"--fsck=no",
   797  		},
   798  	})
   799  }
   800  
   801  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeHappyRealSystemdMount(c *C) {
   802  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   803  
   804  	restore := disks.MockMountPointDisksToPartitionMapping(
   805  		map[disks.Mountpoint]*disks.MockDiskMapping{
   806  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
   807  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
   808  		},
   809  	)
   810  	defer restore()
   811  
   812  	baseMnt := filepath.Join(boot.InitramfsRunMntDir, "base")
   813  	kernelMnt := filepath.Join(boot.InitramfsRunMntDir, "kernel")
   814  
   815  	// don't do anything from systemd-mount, we verify the arguments passed at
   816  	// the end with cmd.Calls
   817  	cmd := testutil.MockCommand(c, "systemd-mount", ``)
   818  	defer cmd.Restore()
   819  
   820  	// mock that in turn, /run/mnt/ubuntu-boot, /run/mnt/ubuntu-seed, etc. are
   821  	// mounted
   822  	n := 0
   823  	restore = main.MockOsutilIsMounted(func(where string) (bool, error) {
   824  		n++
   825  		switch n {
   826  		// first call for each mount returns false, then returns true, this
   827  		// tests in the case where systemd is racy / inconsistent and things
   828  		// aren't mounted by the time systemd-mount returns
   829  		case 1, 2:
   830  			c.Assert(where, Equals, boot.InitramfsUbuntuBootDir)
   831  			return n%2 == 0, nil
   832  		case 3, 4:
   833  			c.Assert(where, Equals, boot.InitramfsUbuntuSeedDir)
   834  			return n%2 == 0, nil
   835  		case 5, 6:
   836  			c.Assert(where, Equals, boot.InitramfsDataDir)
   837  			return n%2 == 0, nil
   838  		case 7, 8:
   839  			c.Assert(where, Equals, baseMnt)
   840  			return n%2 == 0, nil
   841  		case 9, 10:
   842  			c.Assert(where, Equals, kernelMnt)
   843  			return n%2 == 0, nil
   844  		default:
   845  			c.Errorf("unexpected IsMounted check on %s", where)
   846  			return false, fmt.Errorf("unexpected IsMounted check on %s", where)
   847  		}
   848  	})
   849  	defer restore()
   850  
   851  	// mock a bootloader
   852  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   853  	bootloader.Force(bloader)
   854  	defer bootloader.Force(nil)
   855  
   856  	// set the current kernel
   857  	restore = bloader.SetEnabledKernel(s.kernel)
   858  	defer restore()
   859  
   860  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   861  
   862  	// write modeenv
   863  	modeEnv := boot.Modeenv{
   864  		Mode:           "run",
   865  		Base:           s.core20.Filename(),
   866  		CurrentKernels: []string{s.kernel.Filename()},
   867  	}
   868  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   869  	c.Assert(err, IsNil)
   870  
   871  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   872  	c.Assert(err, IsNil)
   873  	c.Check(s.Stdout.String(), Equals, "")
   874  
   875  	// check that all of the override files are present
   876  	for _, initrdUnit := range []string{
   877  		"initrd.target",
   878  		"initrd-fs.target",
   879  		"initrd-switch-root.target",
   880  		"local-fs.target",
   881  	} {
   882  		for _, mountUnit := range []string{
   883  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuBootDir),
   884  			systemd.EscapeUnitNamePath(boot.InitramfsUbuntuSeedDir),
   885  			systemd.EscapeUnitNamePath(boot.InitramfsDataDir),
   886  			systemd.EscapeUnitNamePath(baseMnt),
   887  			systemd.EscapeUnitNamePath(kernelMnt),
   888  		} {
   889  			fname := fmt.Sprintf("snap_bootstrap_%s.conf", mountUnit)
   890  			unitFile := filepath.Join(dirs.GlobalRootDir, "/run/systemd/system", initrdUnit+".d", fname)
   891  			c.Assert(unitFile, testutil.FileEquals, fmt.Sprintf(`[Unit]
   892  Requires=%[1]s
   893  After=%[1]s
   894  `, mountUnit+".mount"))
   895  		}
   896  	}
   897  
   898  	// 2 IsMounted calls per mount point, so 10 total IsMounted calls
   899  	c.Assert(n, Equals, 10)
   900  
   901  	c.Assert(cmd.Calls(), DeepEquals, [][]string{
   902  		{
   903  			"systemd-mount",
   904  			"/dev/disk/by-label/ubuntu-boot",
   905  			boot.InitramfsUbuntuBootDir,
   906  			"--no-pager",
   907  			"--no-ask-password",
   908  			"--fsck=yes",
   909  		},
   910  		{
   911  			"systemd-mount",
   912  			"/dev/disk/by-partuuid/ubuntu-seed-partuuid",
   913  			boot.InitramfsUbuntuSeedDir,
   914  			"--no-pager",
   915  			"--no-ask-password",
   916  			"--fsck=no",
   917  		},
   918  		{
   919  			"systemd-mount",
   920  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
   921  			boot.InitramfsDataDir,
   922  			"--no-pager",
   923  			"--no-ask-password",
   924  			"--fsck=yes",
   925  		},
   926  		{
   927  			"systemd-mount",
   928  			filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), s.core20.Filename()),
   929  			baseMnt,
   930  			"--no-pager",
   931  			"--no-ask-password",
   932  			"--fsck=no",
   933  		},
   934  		{
   935  			"systemd-mount",
   936  			filepath.Join(dirs.SnapBlobDirUnder(boot.InitramfsWritableDir), s.kernel.Filename()),
   937  			kernelMnt,
   938  			"--no-pager",
   939  			"--no-ask-password",
   940  			"--fsck=no",
   941  		},
   942  	})
   943  }
   944  
   945  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeFirstBootRecoverySystemSetHappy(c *C) {
   946  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   947  
   948  	restore := disks.MockMountPointDisksToPartitionMapping(
   949  		map[disks.Mountpoint]*disks.MockDiskMapping{
   950  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
   951  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
   952  		},
   953  	)
   954  	defer restore()
   955  
   956  	restore = s.mockSystemdMountSequence(c, []systemdMount{
   957  		ubuntuLabelMount("ubuntu-boot", "run"),
   958  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
   959  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
   960  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
   961  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
   962  		// RecoverySystem set makes us mount the snapd snap here
   963  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
   964  	}, nil)
   965  	defer restore()
   966  
   967  	// mock a bootloader
   968  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
   969  	bootloader.Force(bloader)
   970  	defer bootloader.Force(nil)
   971  
   972  	// set the current kernel
   973  	restore = bloader.SetEnabledKernel(s.kernel)
   974  	defer restore()
   975  
   976  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
   977  
   978  	// write modeenv
   979  	modeEnv := boot.Modeenv{
   980  		Mode:           "run",
   981  		RecoverySystem: "20191118",
   982  		Base:           s.core20.Filename(),
   983  		CurrentKernels: []string{s.kernel.Filename()},
   984  	}
   985  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
   986  	c.Assert(err, IsNil)
   987  
   988  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
   989  	c.Assert(err, IsNil)
   990  }
   991  
   992  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeWithBootedKernelPartUUIDHappy(c *C) {
   993  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
   994  
   995  	restore := main.MockPartitionUUIDForBootedKernelDisk("ubuntu-boot-partuuid")
   996  	defer restore()
   997  
   998  	restore = disks.MockMountPointDisksToPartitionMapping(
   999  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1000  			{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
  1001  			{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
  1002  		},
  1003  	)
  1004  	defer restore()
  1005  
  1006  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1007  		{
  1008  			"/dev/disk/by-partuuid/ubuntu-boot-partuuid",
  1009  			boot.InitramfsUbuntuBootDir,
  1010  			needsFsckDiskMountOpts,
  1011  		},
  1012  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1013  		ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
  1014  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1015  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1016  	}, nil)
  1017  	defer restore()
  1018  
  1019  	// mock a bootloader
  1020  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1021  	bootloader.Force(bloader)
  1022  	defer bootloader.Force(nil)
  1023  
  1024  	// set the current kernel
  1025  	restore = bloader.SetEnabledKernel(s.kernel)
  1026  	defer restore()
  1027  
  1028  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1029  
  1030  	// write modeenv
  1031  	modeEnv := boot.Modeenv{
  1032  		Mode:           "run",
  1033  		Base:           s.core20.Filename(),
  1034  		CurrentKernels: []string{s.kernel.Filename()},
  1035  	}
  1036  	err := modeEnv.WriteTo(boot.InitramfsWritableDir)
  1037  	c.Assert(err, IsNil)
  1038  
  1039  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1040  	c.Assert(err, IsNil)
  1041  }
  1042  
  1043  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedDataHappy(c *C) {
  1044  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1045  
  1046  	restore := disks.MockMountPointDisksToPartitionMapping(
  1047  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1048  			{Mountpoint: boot.InitramfsUbuntuBootDir}:                    defaultEncBootDisk,
  1049  			{Mountpoint: boot.InitramfsDataDir, IsDecryptedDevice: true}: defaultEncBootDisk,
  1050  		},
  1051  	)
  1052  	defer restore()
  1053  
  1054  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1055  		ubuntuLabelMount("ubuntu-boot", "run"),
  1056  		ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1057  		{
  1058  			"path-to-device",
  1059  			boot.InitramfsDataDir,
  1060  			needsFsckDiskMountOpts,
  1061  		},
  1062  		s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1063  		s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1064  	}, nil)
  1065  	defer restore()
  1066  
  1067  	// write the installed model like makebootable does it
  1068  	err := os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755)
  1069  	c.Assert(err, IsNil)
  1070  	mf, err := os.Create(filepath.Join(boot.InitramfsUbuntuBootDir, "model"))
  1071  	c.Assert(err, IsNil)
  1072  	defer mf.Close()
  1073  	err = asserts.NewEncoder(mf).Encode(s.model)
  1074  	c.Assert(err, IsNil)
  1075  
  1076  	activated := false
  1077  	restore = main.MockSecbootUnlockVolumeIfEncrypted(func(disk disks.Disk, name string, encryptionKeyDir string, lockKeysOnFinish bool) (string, bool, error) {
  1078  		c.Assert(name, Equals, "ubuntu-data")
  1079  		c.Assert(encryptionKeyDir, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde"))
  1080  		c.Assert(lockKeysOnFinish, Equals, true)
  1081  		activated = true
  1082  		// return true because we are using an encrypted device
  1083  		return "path-to-device", true, nil
  1084  	})
  1085  	defer restore()
  1086  
  1087  	measureEpochCalls := 0
  1088  	measureModelCalls := 0
  1089  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  1090  		measureEpochCalls++
  1091  		return nil
  1092  	})
  1093  	defer restore()
  1094  
  1095  	var measuredModel *asserts.Model
  1096  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  1097  		measureModelCalls++
  1098  		var err error
  1099  		measuredModel, err = findModel()
  1100  		if err != nil {
  1101  			return err
  1102  		}
  1103  		return nil
  1104  	})
  1105  	defer restore()
  1106  
  1107  	// mock a bootloader
  1108  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1109  	bootloader.Force(bloader)
  1110  	defer bootloader.Force(nil)
  1111  
  1112  	// set the current kernel
  1113  	restore = bloader.SetEnabledKernel(s.kernel)
  1114  	defer restore()
  1115  
  1116  	makeSnapFilesOnEarlyBootUbuntuData(c, s.kernel, s.core20)
  1117  
  1118  	// write modeenv
  1119  	modeEnv := boot.Modeenv{
  1120  		Mode:           "run",
  1121  		Base:           s.core20.Filename(),
  1122  		CurrentKernels: []string{s.kernel.Filename()},
  1123  	}
  1124  	err = modeEnv.WriteTo(boot.InitramfsWritableDir)
  1125  	c.Assert(err, IsNil)
  1126  
  1127  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1128  	c.Assert(err, IsNil)
  1129  	c.Check(activated, Equals, true)
  1130  	c.Check(measureEpochCalls, Equals, 1)
  1131  	c.Check(measureModelCalls, Equals, 1)
  1132  	c.Check(measuredModel, DeepEquals, s.model)
  1133  
  1134  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  1135  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "run-model-measured"), testutil.FilePresent)
  1136  }
  1137  
  1138  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeEncryptedNoModel(c *C) {
  1139  	s.testInitramfsMountsEncryptedNoModel(c, "run", "", 1)
  1140  }
  1141  
  1142  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeEncryptedNoModel(c *C) {
  1143  	s.testInitramfsMountsEncryptedNoModel(c, "install", s.sysLabel, 0)
  1144  }
  1145  
  1146  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedNoModel(c *C) {
  1147  	s.testInitramfsMountsEncryptedNoModel(c, "recover", s.sysLabel, 0)
  1148  }
  1149  
  1150  func (s *initramfsMountsSuite) testInitramfsMountsEncryptedNoModel(c *C, mode, label string, expectedMeasureModelCalls int) {
  1151  	s.mockProcCmdlineContent(c, fmt.Sprintf("snapd_recovery_mode=%s", mode))
  1152  
  1153  	// install and recover mounts are just ubuntu-seed before we fail
  1154  	var restore func()
  1155  	if mode == "run" {
  1156  		// run mode will mount ubuntu-boot and ubuntu-seed
  1157  		restore = s.mockSystemdMountSequence(c, []systemdMount{
  1158  			ubuntuLabelMount("ubuntu-boot", mode),
  1159  			ubuntuPartUUIDMount("ubuntu-seed-partuuid", mode),
  1160  		}, nil)
  1161  		restore2 := disks.MockMountPointDisksToPartitionMapping(
  1162  			map[disks.Mountpoint]*disks.MockDiskMapping{
  1163  				{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultEncBootDisk,
  1164  			},
  1165  		)
  1166  		defer restore2()
  1167  	} else {
  1168  		restore = s.mockSystemdMountSequence(c, []systemdMount{
  1169  			ubuntuLabelMount("ubuntu-seed", mode),
  1170  		}, nil)
  1171  
  1172  		// in install / recover mode the code doesn't make it far enough to do
  1173  		// any disk cross checking
  1174  	}
  1175  	defer restore()
  1176  
  1177  	if label != "" {
  1178  		s.mockProcCmdlineContent(c,
  1179  			fmt.Sprintf("snapd_recovery_mode=%s snapd_recovery_system=%s", mode, label))
  1180  		// break the seed
  1181  		err := os.Remove(filepath.Join(s.seedDir, "systems", label, "model"))
  1182  		c.Assert(err, IsNil)
  1183  	}
  1184  
  1185  	measureEpochCalls := 0
  1186  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  1187  		measureEpochCalls++
  1188  		return nil
  1189  	})
  1190  	defer restore()
  1191  
  1192  	measureModelCalls := 0
  1193  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  1194  		measureModelCalls++
  1195  		_, err := findModel()
  1196  		if err != nil {
  1197  			return err
  1198  		}
  1199  		return fmt.Errorf("unexpected call")
  1200  	})
  1201  	defer restore()
  1202  
  1203  	_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1204  	where := "/run/mnt/ubuntu-boot/model"
  1205  	if mode != "run" {
  1206  		where = fmt.Sprintf("/run/mnt/ubuntu-seed/systems/%s/model", label)
  1207  	}
  1208  	c.Assert(err, ErrorMatches, fmt.Sprintf(".*cannot read model assertion: open .*%s: no such file or directory", where))
  1209  	c.Assert(measureEpochCalls, Equals, 1)
  1210  	c.Assert(measureModelCalls, Equals, expectedMeasureModelCalls)
  1211  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  1212  	gl, err := filepath.Glob(filepath.Join(dirs.SnapBootstrapRunDir, "*-model-measured"))
  1213  	c.Assert(err, IsNil)
  1214  	c.Assert(gl, HasLen, 0)
  1215  }
  1216  
  1217  func (s *initramfsMountsSuite) TestInitramfsMountsRunModeUpgradeScenarios(c *C) {
  1218  	tt := []struct {
  1219  		modeenv *boot.Modeenv
  1220  		// this is a function so we can have delayed execution, typical values
  1221  		// depend on the root dir which changes for each test case
  1222  		additionalMountsFunc func() []systemdMount
  1223  		enableKernel         snap.PlaceInfo
  1224  		enableTryKernel      snap.PlaceInfo
  1225  		snapFiles            []snap.PlaceInfo
  1226  		kernelStatus         string
  1227  
  1228  		expRebootPanic string
  1229  		expLog         string
  1230  		expError       string
  1231  		expModeenv     *boot.Modeenv
  1232  		comment        string
  1233  	}{
  1234  		// default case no upgrades
  1235  		{
  1236  			modeenv: &boot.Modeenv{
  1237  				Mode:           "run",
  1238  				Base:           s.core20.Filename(),
  1239  				CurrentKernels: []string{s.kernel.Filename()},
  1240  			},
  1241  			additionalMountsFunc: func() []systemdMount {
  1242  				return []systemdMount{
  1243  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1244  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1245  				}
  1246  			},
  1247  			enableKernel: s.kernel,
  1248  			snapFiles:    []snap.PlaceInfo{s.core20, s.kernel},
  1249  			comment:      "happy default no upgrades",
  1250  		},
  1251  
  1252  		// happy upgrade cases
  1253  		{
  1254  			modeenv: &boot.Modeenv{
  1255  				Mode:           "run",
  1256  				Base:           s.core20.Filename(),
  1257  				CurrentKernels: []string{s.kernel.Filename(), s.kernelr2.Filename()},
  1258  			},
  1259  			additionalMountsFunc: func() []systemdMount {
  1260  				return []systemdMount{
  1261  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1262  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernelr2),
  1263  				}
  1264  			},
  1265  			kernelStatus:    boot.TryingStatus,
  1266  			enableKernel:    s.kernel,
  1267  			enableTryKernel: s.kernelr2,
  1268  			snapFiles:       []snap.PlaceInfo{s.core20, s.kernel, s.kernelr2},
  1269  			comment:         "happy kernel snap upgrade",
  1270  		},
  1271  		{
  1272  			modeenv: &boot.Modeenv{
  1273  				Mode:           "run",
  1274  				Base:           s.core20.Filename(),
  1275  				TryBase:        s.core20r2.Filename(),
  1276  				BaseStatus:     boot.TryStatus,
  1277  				CurrentKernels: []string{s.kernel.Filename()},
  1278  			},
  1279  			additionalMountsFunc: func() []systemdMount {
  1280  				return []systemdMount{
  1281  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20r2),
  1282  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1283  				}
  1284  			},
  1285  			enableKernel: s.kernel,
  1286  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20, s.core20r2},
  1287  			expModeenv: &boot.Modeenv{
  1288  				Mode:           "run",
  1289  				Base:           s.core20.Filename(),
  1290  				TryBase:        s.core20r2.Filename(),
  1291  				BaseStatus:     boot.TryingStatus,
  1292  				CurrentKernels: []string{s.kernel.Filename()},
  1293  			},
  1294  			comment: "happy base snap upgrade",
  1295  		},
  1296  		{
  1297  			modeenv: &boot.Modeenv{
  1298  				Mode:           "run",
  1299  				Base:           s.core20.Filename(),
  1300  				TryBase:        s.core20r2.Filename(),
  1301  				BaseStatus:     boot.TryStatus,
  1302  				CurrentKernels: []string{s.kernel.Filename(), s.kernelr2.Filename()},
  1303  			},
  1304  			additionalMountsFunc: func() []systemdMount {
  1305  				return []systemdMount{
  1306  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20r2),
  1307  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernelr2),
  1308  				}
  1309  			},
  1310  			enableKernel:    s.kernel,
  1311  			enableTryKernel: s.kernelr2,
  1312  			snapFiles:       []snap.PlaceInfo{s.kernel, s.kernelr2, s.core20, s.core20r2},
  1313  			kernelStatus:    boot.TryingStatus,
  1314  			expModeenv: &boot.Modeenv{
  1315  				Mode:           "run",
  1316  				Base:           s.core20.Filename(),
  1317  				TryBase:        s.core20r2.Filename(),
  1318  				BaseStatus:     boot.TryingStatus,
  1319  				CurrentKernels: []string{s.kernel.Filename(), s.kernelr2.Filename()},
  1320  			},
  1321  			comment: "happy simultaneous base snap and kernel snap upgrade",
  1322  		},
  1323  
  1324  		// fallback cases
  1325  		{
  1326  			modeenv: &boot.Modeenv{
  1327  				Mode:           "run",
  1328  				Base:           s.core20.Filename(),
  1329  				TryBase:        s.core20r2.Filename(),
  1330  				BaseStatus:     boot.TryStatus,
  1331  				CurrentKernels: []string{s.kernel.Filename()},
  1332  			},
  1333  			additionalMountsFunc: func() []systemdMount {
  1334  				return []systemdMount{
  1335  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1336  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1337  				}
  1338  			},
  1339  			enableKernel: s.kernel,
  1340  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20},
  1341  			comment:      "happy fallback try base not existing",
  1342  		},
  1343  		{
  1344  			modeenv: &boot.Modeenv{
  1345  				Mode:           "run",
  1346  				Base:           s.core20.Filename(),
  1347  				BaseStatus:     boot.TryStatus,
  1348  				TryBase:        "",
  1349  				CurrentKernels: []string{s.kernel.Filename()},
  1350  			},
  1351  			additionalMountsFunc: func() []systemdMount {
  1352  				return []systemdMount{
  1353  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1354  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1355  				}
  1356  			},
  1357  			enableKernel: s.kernel,
  1358  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20},
  1359  			comment:      "happy fallback base_status try, empty try_base",
  1360  		},
  1361  		{
  1362  			modeenv: &boot.Modeenv{
  1363  				Mode:           "run",
  1364  				Base:           s.core20.Filename(),
  1365  				TryBase:        s.core20r2.Filename(),
  1366  				BaseStatus:     boot.TryingStatus,
  1367  				CurrentKernels: []string{s.kernel.Filename()},
  1368  			},
  1369  			additionalMountsFunc: func() []systemdMount {
  1370  				return []systemdMount{
  1371  					s.makeRunSnapSystemdMount(snap.TypeBase, s.core20),
  1372  					s.makeRunSnapSystemdMount(snap.TypeKernel, s.kernel),
  1373  				}
  1374  			},
  1375  			enableKernel: s.kernel,
  1376  			snapFiles:    []snap.PlaceInfo{s.kernel, s.core20, s.core20r2},
  1377  			expModeenv: &boot.Modeenv{
  1378  				Mode:           "run",
  1379  				Base:           s.core20.Filename(),
  1380  				TryBase:        s.core20r2.Filename(),
  1381  				BaseStatus:     boot.DefaultStatus,
  1382  				CurrentKernels: []string{s.kernel.Filename()},
  1383  			},
  1384  			comment: "happy fallback failed boot with try snap",
  1385  		},
  1386  		{
  1387  			modeenv: &boot.Modeenv{
  1388  				Mode:           "run",
  1389  				Base:           s.core20.Filename(),
  1390  				CurrentKernels: []string{s.kernel.Filename()},
  1391  			},
  1392  			enableKernel:    s.kernel,
  1393  			enableTryKernel: s.kernelr2,
  1394  			snapFiles:       []snap.PlaceInfo{s.core20, s.kernel, s.kernelr2},
  1395  			kernelStatus:    boot.TryingStatus,
  1396  			expRebootPanic:  "reboot due to untrusted try kernel snap",
  1397  			comment:         "happy fallback untrusted try kernel snap",
  1398  		},
  1399  		// TODO:UC20: if we ever have a way to compare what kernel was booted,
  1400  		//            and we compute that the booted kernel was the try kernel,
  1401  		//            but the try kernel is not enabled on the bootloader
  1402  		//            (somehow??), then this should become a reboot case rather
  1403  		//            than mount the old kernel snap
  1404  		{
  1405  			modeenv: &boot.Modeenv{
  1406  				Mode:           "run",
  1407  				Base:           s.core20.Filename(),
  1408  				CurrentKernels: []string{s.kernel.Filename()},
  1409  			},
  1410  			kernelStatus:   boot.TryingStatus,
  1411  			enableKernel:   s.kernel,
  1412  			snapFiles:      []snap.PlaceInfo{s.core20, s.kernel},
  1413  			expRebootPanic: "reboot due to no try kernel snap",
  1414  			comment:        "happy fallback kernel_status trying no try kernel",
  1415  		},
  1416  
  1417  		// unhappy cases
  1418  		{
  1419  			modeenv: &boot.Modeenv{
  1420  				Mode: "run",
  1421  			},
  1422  			expError: "fallback base snap unusable: cannot get snap revision: modeenv base boot variable is empty",
  1423  			comment:  "unhappy empty modeenv",
  1424  		},
  1425  		// TODO:UC20: in this case snap-bootstrap should request a reboot, since we
  1426  		//            already booted the try snap, so mounting the fallback kernel will
  1427  		//            not match in some cases
  1428  		{
  1429  			modeenv: &boot.Modeenv{
  1430  				Mode:           "run",
  1431  				Base:           s.core20.Filename(),
  1432  				CurrentKernels: []string{s.kernel.Filename()},
  1433  			},
  1434  			enableKernel: s.kernelr2,
  1435  			snapFiles:    []snap.PlaceInfo{s.core20, s.kernelr2},
  1436  			expError:     fmt.Sprintf("fallback kernel snap %q is not trusted in the modeenv", s.kernelr2.Filename()),
  1437  			comment:      "unhappy untrusted main kernel snap",
  1438  		},
  1439  	}
  1440  
  1441  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=run")
  1442  
  1443  	for _, t := range tt {
  1444  		comment := Commentf(t.comment)
  1445  
  1446  		var cleanups []func()
  1447  
  1448  		if t.expRebootPanic != "" {
  1449  			r := boot.MockInitramfsReboot(func() error {
  1450  				panic(t.expRebootPanic)
  1451  			})
  1452  			cleanups = append(cleanups, r)
  1453  		}
  1454  
  1455  		// setup unique root dir per test
  1456  		rootDir := c.MkDir()
  1457  		cleanups = append(cleanups, func() { dirs.SetRootDir(dirs.GlobalRootDir) })
  1458  		dirs.SetRootDir(rootDir)
  1459  
  1460  		restore := disks.MockMountPointDisksToPartitionMapping(
  1461  			map[disks.Mountpoint]*disks.MockDiskMapping{
  1462  				{Mountpoint: boot.InitramfsUbuntuBootDir}: defaultBootDisk,
  1463  				{Mountpoint: boot.InitramfsDataDir}:       defaultBootDisk,
  1464  			},
  1465  		)
  1466  		cleanups = append(cleanups, restore)
  1467  
  1468  		// setup expected systemd-mount calls - every test case has ubuntu-boot,
  1469  		// ubuntu-seed and ubuntu-data mounts because all those mounts happen
  1470  		// before any boot logic
  1471  		mnts := []systemdMount{
  1472  			ubuntuLabelMount("ubuntu-boot", "run"),
  1473  			ubuntuPartUUIDMount("ubuntu-seed-partuuid", "run"),
  1474  			ubuntuPartUUIDMount("ubuntu-data-partuuid", "run"),
  1475  		}
  1476  		if t.additionalMountsFunc != nil {
  1477  			mnts = append(mnts, t.additionalMountsFunc()...)
  1478  		}
  1479  		cleanups = append(cleanups, s.mockSystemdMountSequence(c, mnts, comment))
  1480  
  1481  		// mock a bootloader
  1482  		bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  1483  		bootloader.Force(bloader)
  1484  		cleanups = append(cleanups, func() { bootloader.Force(nil) })
  1485  
  1486  		if t.enableKernel != nil {
  1487  			// don't need to restore since each test case has a unique bloader
  1488  			bloader.SetEnabledKernel(t.enableKernel)
  1489  		}
  1490  
  1491  		if t.enableTryKernel != nil {
  1492  			bloader.SetEnabledTryKernel(t.enableTryKernel)
  1493  		}
  1494  
  1495  		// set the kernel_status boot var
  1496  		err := bloader.SetBootVars(map[string]string{"kernel_status": t.kernelStatus})
  1497  		c.Assert(err, IsNil, comment)
  1498  
  1499  		// write the initial modeenv
  1500  		err = t.modeenv.WriteTo(boot.InitramfsWritableDir)
  1501  		c.Assert(err, IsNil, comment)
  1502  
  1503  		// make the snap files - no restore needed because we use a unique root
  1504  		// dir for each test case
  1505  		makeSnapFilesOnEarlyBootUbuntuData(c, t.snapFiles...)
  1506  
  1507  		if t.expRebootPanic != "" {
  1508  			f := func() { main.Parser().ParseArgs([]string{"initramfs-mounts"}) }
  1509  			c.Assert(f, PanicMatches, t.expRebootPanic, comment)
  1510  		} else {
  1511  			_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1512  			if t.expError != "" {
  1513  				c.Assert(err, ErrorMatches, t.expError, comment)
  1514  			} else {
  1515  				c.Assert(err, IsNil, comment)
  1516  
  1517  				// check the resultant modeenv
  1518  				// if the expModeenv is nil, we just compare to the start
  1519  				newModeenv, err := boot.ReadModeenv(boot.InitramfsWritableDir)
  1520  				c.Assert(err, IsNil, comment)
  1521  				m := t.modeenv
  1522  				if t.expModeenv != nil {
  1523  					m = t.expModeenv
  1524  				}
  1525  				c.Assert(newModeenv.BaseStatus, DeepEquals, m.BaseStatus, comment)
  1526  				c.Assert(newModeenv.TryBase, DeepEquals, m.TryBase, comment)
  1527  				c.Assert(newModeenv.Base, DeepEquals, m.Base, comment)
  1528  			}
  1529  		}
  1530  
  1531  		for _, r := range cleanups {
  1532  			r()
  1533  		}
  1534  	}
  1535  }
  1536  
  1537  func (s *initramfsMountsSuite) testRecoverModeHappy(c *C) {
  1538  	// mock various files that are copied around during recover mode (and files
  1539  	// that shouldn't be copied around)
  1540  	ephemeralUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "data/")
  1541  	err := os.MkdirAll(ephemeralUbuntuData, 0755)
  1542  	c.Assert(err, IsNil)
  1543  	// mock a auth data in the host's ubuntu-data
  1544  	hostUbuntuData := filepath.Join(boot.InitramfsRunMntDir, "host/ubuntu-data/")
  1545  	err = os.MkdirAll(hostUbuntuData, 0755)
  1546  	c.Assert(err, IsNil)
  1547  	mockCopiedFiles := []string{
  1548  		// extrausers
  1549  		"system-data/var/lib/extrausers/passwd",
  1550  		"system-data/var/lib/extrausers/shadow",
  1551  		"system-data/var/lib/extrausers/group",
  1552  		"system-data/var/lib/extrausers/gshadow",
  1553  		// sshd
  1554  		"system-data/etc/ssh/ssh_host_rsa.key",
  1555  		"system-data/etc/ssh/ssh_host_rsa.key.pub",
  1556  		// user ssh
  1557  		"user-data/user1/.ssh/authorized_keys",
  1558  		"user-data/user2/.ssh/authorized_keys",
  1559  		// user snap authentication
  1560  		"user-data/user1/.snap/auth.json",
  1561  		// sudoers
  1562  		"system-data/etc/sudoers.d/create-user-test",
  1563  		// netplan networking
  1564  		"system-data/etc/netplan/00-snapd-config.yaml", // example console-conf filename
  1565  		"system-data/etc/netplan/50-cloud-init.yaml",   // example cloud-init filename
  1566  		// systemd clock file
  1567  		"system-data/var/lib/systemd/timesync/clock",
  1568  	}
  1569  	mockUnrelatedFiles := []string{
  1570  		"system-data/var/lib/foo",
  1571  		"system-data/etc/passwd",
  1572  		"user-data/user1/some-random-data",
  1573  		"user-data/user2/other-random-data",
  1574  		"user-data/user2/.snap/sneaky-not-auth.json",
  1575  		"system-data/etc/not-networking/netplan",
  1576  		"system-data/var/lib/systemd/timesync/clock-not-the-clock",
  1577  	}
  1578  	for _, mockFile := range append(mockCopiedFiles, mockUnrelatedFiles...) {
  1579  		p := filepath.Join(hostUbuntuData, mockFile)
  1580  		err = os.MkdirAll(filepath.Dir(p), 0750)
  1581  		c.Assert(err, IsNil)
  1582  		mockContent := fmt.Sprintf("content of %s", filepath.Base(mockFile))
  1583  		err = ioutil.WriteFile(p, []byte(mockContent), 0640)
  1584  		c.Assert(err, IsNil)
  1585  	}
  1586  	// create a mock state
  1587  	mockedState := filepath.Join(hostUbuntuData, "system-data/var/lib/snapd/state.json")
  1588  	err = os.MkdirAll(filepath.Dir(mockedState), 0750)
  1589  	c.Assert(err, IsNil)
  1590  	err = ioutil.WriteFile(mockedState, []byte(mockStateContent), 0640)
  1591  	c.Assert(err, IsNil)
  1592  
  1593  	_, err = main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1594  	c.Assert(err, IsNil)
  1595  
  1596  	modeEnv := filepath.Join(ephemeralUbuntuData, "/system-data/var/lib/snapd/modeenv")
  1597  	c.Check(modeEnv, testutil.FileEquals, `mode=recover
  1598  recovery_system=20191118
  1599  `)
  1600  	for _, p := range mockUnrelatedFiles {
  1601  		c.Check(filepath.Join(ephemeralUbuntuData, p), testutil.FileAbsent)
  1602  	}
  1603  	for _, p := range mockCopiedFiles {
  1604  		c.Check(filepath.Join(ephemeralUbuntuData, p), testutil.FilePresent)
  1605  		fi, err := os.Stat(filepath.Join(ephemeralUbuntuData, p))
  1606  		// check file mode is set
  1607  		c.Assert(err, IsNil)
  1608  		c.Check(fi.Mode(), Equals, os.FileMode(0640))
  1609  		// check dir mode is set in parent dir
  1610  		fiParent, err := os.Stat(filepath.Dir(filepath.Join(ephemeralUbuntuData, p)))
  1611  		c.Assert(err, IsNil)
  1612  		c.Check(fiParent.Mode(), Equals, os.FileMode(os.ModeDir|0750))
  1613  	}
  1614  
  1615  	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}`)
  1616  
  1617  	// finally check that the recovery system bootenv was updated to be in run
  1618  	// mode
  1619  	bloader, err := bootloader.Find("", nil)
  1620  	c.Assert(err, IsNil)
  1621  	m, err := bloader.GetBootVars("snapd_recovery_system", "snapd_recovery_mode")
  1622  	c.Assert(err, IsNil)
  1623  	c.Assert(m, DeepEquals, map[string]string{
  1624  		"snapd_recovery_system": "20191118",
  1625  		"snapd_recovery_mode":   "run",
  1626  	})
  1627  }
  1628  
  1629  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappy(c *C) {
  1630  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  1631  
  1632  	// setup a bootloader for setting the bootenv after we are done
  1633  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1634  	bootloader.Force(bloader)
  1635  	defer bootloader.Force(nil)
  1636  
  1637  	// mock that we don't know which partition uuid the kernel was booted from
  1638  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  1639  	defer restore()
  1640  
  1641  	restore = disks.MockMountPointDisksToPartitionMapping(
  1642  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1643  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootDisk,
  1644  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootDisk,
  1645  		},
  1646  	)
  1647  	defer restore()
  1648  
  1649  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1650  		ubuntuLabelMount("ubuntu-seed", "recover"),
  1651  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  1652  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  1653  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  1654  		{
  1655  			"tmpfs",
  1656  			boot.InitramfsDataDir,
  1657  			tmpfsMountOpts,
  1658  		},
  1659  		{
  1660  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1661  			boot.InitramfsHostUbuntuDataDir,
  1662  			nil,
  1663  		},
  1664  	}, nil)
  1665  	defer restore()
  1666  
  1667  	s.testRecoverModeHappy(c)
  1668  }
  1669  
  1670  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappyBootedKernelPartitionUUID(c *C) {
  1671  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  1672  
  1673  	restore := main.MockPartitionUUIDForBootedKernelDisk("specific-ubuntu-seed-partuuid")
  1674  	defer restore()
  1675  
  1676  	// setup a bootloader for setting the bootenv after we are done
  1677  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1678  	bootloader.Force(bloader)
  1679  	defer bootloader.Force(nil)
  1680  
  1681  	restore = disks.MockMountPointDisksToPartitionMapping(
  1682  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1683  			{Mountpoint: boot.InitramfsUbuntuSeedDir}:     defaultBootDisk,
  1684  			{Mountpoint: boot.InitramfsHostUbuntuDataDir}: defaultBootDisk,
  1685  		},
  1686  	)
  1687  	defer restore()
  1688  
  1689  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1690  		{
  1691  			"/dev/disk/by-partuuid/specific-ubuntu-seed-partuuid",
  1692  			boot.InitramfsUbuntuSeedDir,
  1693  			needsFsckDiskMountOpts,
  1694  		},
  1695  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  1696  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  1697  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  1698  		{
  1699  			"tmpfs",
  1700  			boot.InitramfsDataDir,
  1701  			tmpfsMountOpts,
  1702  		},
  1703  		{
  1704  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1705  			boot.InitramfsHostUbuntuDataDir,
  1706  			nil,
  1707  		},
  1708  	}, nil)
  1709  	defer restore()
  1710  
  1711  	s.testRecoverModeHappy(c)
  1712  }
  1713  
  1714  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeHappyEncrypted(c *C) {
  1715  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  1716  
  1717  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  1718  	defer restore()
  1719  
  1720  	// setup a bootloader for setting the bootenv after we are done
  1721  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1722  	bootloader.Force(bloader)
  1723  	defer bootloader.Force(nil)
  1724  
  1725  	restore = disks.MockMountPointDisksToPartitionMapping(
  1726  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1727  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: defaultEncBootDisk,
  1728  			{
  1729  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  1730  				IsDecryptedDevice: true,
  1731  			}: defaultEncBootDisk,
  1732  		},
  1733  	)
  1734  	defer restore()
  1735  
  1736  	activated := false
  1737  	restore = main.MockSecbootUnlockVolumeIfEncrypted(func(disk disks.Disk, name string, encryptionKeyDir string, lockKeysOnFinish bool) (string, bool, error) {
  1738  		c.Assert(name, Equals, "ubuntu-data")
  1739  		c.Assert(encryptionKeyDir, Equals, filepath.Join(s.tmpDir, "run/mnt/ubuntu-seed/device/fde"))
  1740  		encDevPartUUID, err := disk.FindMatchingPartitionUUID(name + "-enc")
  1741  		c.Assert(err, IsNil)
  1742  		c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  1743  		c.Assert(lockKeysOnFinish, Equals, true)
  1744  		activated = true
  1745  		return filepath.Join("/dev/disk/by-partuuid", encDevPartUUID), true, nil
  1746  	})
  1747  	defer restore()
  1748  
  1749  	measureEpochCalls := 0
  1750  	measureModelCalls := 0
  1751  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  1752  		measureEpochCalls++
  1753  		return nil
  1754  	})
  1755  	defer restore()
  1756  
  1757  	var measuredModel *asserts.Model
  1758  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  1759  		measureModelCalls++
  1760  		var err error
  1761  		measuredModel, err = findModel()
  1762  		if err != nil {
  1763  			return err
  1764  		}
  1765  		return nil
  1766  	})
  1767  	defer restore()
  1768  
  1769  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1770  		ubuntuLabelMount("ubuntu-seed", "recover"),
  1771  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  1772  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  1773  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  1774  		{
  1775  			"tmpfs",
  1776  			boot.InitramfsDataDir,
  1777  			tmpfsMountOpts,
  1778  		},
  1779  		{
  1780  			"/dev/disk/by-partuuid/ubuntu-data-enc-partuuid",
  1781  			boot.InitramfsHostUbuntuDataDir,
  1782  			nil,
  1783  		},
  1784  	}, nil)
  1785  	defer restore()
  1786  
  1787  	s.testRecoverModeHappy(c)
  1788  
  1789  	c.Check(activated, Equals, true)
  1790  	c.Check(measureEpochCalls, Equals, 1)
  1791  	c.Check(measureModelCalls, Equals, 1)
  1792  	c.Check(measuredModel, DeepEquals, s.model)
  1793  
  1794  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  1795  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  1796  }
  1797  
  1798  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeEncryptedAttackerFSAttachedHappy(c *C) {
  1799  	s.mockProcCmdlineContent(c, "snapd_recovery_mode=recover snapd_recovery_system="+s.sysLabel)
  1800  
  1801  	restore := main.MockPartitionUUIDForBootedKernelDisk("")
  1802  	defer restore()
  1803  
  1804  	// setup a bootloader for setting the bootenv
  1805  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1806  	bootloader.Force(bloader)
  1807  	defer bootloader.Force(nil)
  1808  
  1809  	mockDisk := &disks.MockDiskMapping{
  1810  		FilesystemLabelToPartUUID: map[string]string{
  1811  			"ubuntu-seed":     "ubuntu-seed-partuuid",
  1812  			"ubuntu-data-enc": "ubuntu-data-enc-partuuid",
  1813  		},
  1814  		DiskHasPartitions: true,
  1815  		DevNum:            "bootDev",
  1816  	}
  1817  
  1818  	restore = disks.MockMountPointDisksToPartitionMapping(
  1819  		map[disks.Mountpoint]*disks.MockDiskMapping{
  1820  			{Mountpoint: boot.InitramfsUbuntuSeedDir}: mockDisk,
  1821  			{
  1822  				Mountpoint:        boot.InitramfsHostUbuntuDataDir,
  1823  				IsDecryptedDevice: true,
  1824  			}: mockDisk,
  1825  			// this is the attacker fs on a different disk
  1826  			{Mountpoint: "somewhere-else"}: {
  1827  				FilesystemLabelToPartUUID: map[string]string{
  1828  					"ubuntu-seed":     "ubuntu-seed-attacker-partuuid",
  1829  					"ubuntu-data-enc": "ubuntu-data-enc-attacker-partuuid",
  1830  				},
  1831  				DiskHasPartitions: true,
  1832  				DevNum:            "attackerDev",
  1833  			},
  1834  		},
  1835  	)
  1836  	defer restore()
  1837  
  1838  	activated := false
  1839  	restore = main.MockSecbootUnlockVolumeIfEncrypted(func(disk disks.Disk, name string, encryptionKeyDir string, lockKeysOnFinish bool) (string, bool, error) {
  1840  		c.Assert(name, Equals, "ubuntu-data")
  1841  		encDevPartUUID, err := disk.FindMatchingPartitionUUID(name + "-enc")
  1842  		c.Assert(err, IsNil)
  1843  		c.Assert(encDevPartUUID, Equals, "ubuntu-data-enc-partuuid")
  1844  		c.Assert(lockKeysOnFinish, Equals, true)
  1845  		activated = true
  1846  		return filepath.Join("/dev/disk/by-partuuid", encDevPartUUID), true, nil
  1847  	})
  1848  	defer restore()
  1849  
  1850  	measureEpochCalls := 0
  1851  	measureModelCalls := 0
  1852  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  1853  		measureEpochCalls++
  1854  		return nil
  1855  	})
  1856  	defer restore()
  1857  
  1858  	var measuredModel *asserts.Model
  1859  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  1860  		measureModelCalls++
  1861  		var err error
  1862  		measuredModel, err = findModel()
  1863  		if err != nil {
  1864  			return err
  1865  		}
  1866  		return nil
  1867  	})
  1868  	defer restore()
  1869  
  1870  	restore = s.mockSystemdMountSequence(c, []systemdMount{
  1871  		ubuntuLabelMount("ubuntu-seed", "recover"),
  1872  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  1873  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  1874  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  1875  		{
  1876  			"tmpfs",
  1877  			boot.InitramfsDataDir,
  1878  			tmpfsMountOpts,
  1879  		},
  1880  		{
  1881  			"/dev/disk/by-partuuid/ubuntu-data-enc-partuuid",
  1882  			boot.InitramfsHostUbuntuDataDir,
  1883  			nil,
  1884  		},
  1885  	}, nil)
  1886  	defer restore()
  1887  
  1888  	s.testRecoverModeHappy(c)
  1889  
  1890  	c.Check(activated, Equals, true)
  1891  	c.Check(measureEpochCalls, Equals, 1)
  1892  	c.Check(measureModelCalls, Equals, 1)
  1893  	c.Check(measuredModel, DeepEquals, s.model)
  1894  
  1895  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  1896  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, fmt.Sprintf("%s-model-measured", s.sysLabel)), testutil.FilePresent)
  1897  }
  1898  
  1899  func (s *initramfsMountsSuite) testInitramfsMountsInstallRecoverModeMeasure(c *C, mode string) {
  1900  	s.mockProcCmdlineContent(c, fmt.Sprintf("snapd_recovery_mode=%s snapd_recovery_system=%s", mode, s.sysLabel))
  1901  
  1902  	modeMnts := []systemdMount{
  1903  		ubuntuLabelMount("ubuntu-seed", mode),
  1904  		s.makeSeedSnapSystemdMount(snap.TypeSnapd),
  1905  		s.makeSeedSnapSystemdMount(snap.TypeKernel),
  1906  		s.makeSeedSnapSystemdMount(snap.TypeBase),
  1907  		{
  1908  			"tmpfs",
  1909  			boot.InitramfsDataDir,
  1910  			tmpfsMountOpts,
  1911  		},
  1912  	}
  1913  
  1914  	mockDiskMapping := map[disks.Mountpoint]*disks.MockDiskMapping{
  1915  		{Mountpoint: boot.InitramfsUbuntuSeedDir}: {
  1916  			FilesystemLabelToPartUUID: map[string]string{
  1917  				"ubuntu-seed": "ubuntu-seed-partuuid",
  1918  			},
  1919  			DiskHasPartitions: true,
  1920  		},
  1921  	}
  1922  
  1923  	if mode == "recover" {
  1924  		// setup a bootloader for setting the bootenv after we are done
  1925  		bloader := bootloadertest.Mock("mock", c.MkDir())
  1926  		bootloader.Force(bloader)
  1927  		defer bootloader.Force(nil)
  1928  
  1929  		// add the expected mount of ubuntu-data onto the host data dir
  1930  		modeMnts = append(modeMnts, systemdMount{
  1931  			"/dev/disk/by-partuuid/ubuntu-data-partuuid",
  1932  			boot.InitramfsHostUbuntuDataDir,
  1933  			nil,
  1934  		})
  1935  
  1936  		// also add the ubuntu-data fs label to the disk referenced by the
  1937  		// ubuntu-seed partition
  1938  		disk := mockDiskMapping[disks.Mountpoint{Mountpoint: boot.InitramfsUbuntuSeedDir}]
  1939  		disk.FilesystemLabelToPartUUID["ubuntu-data"] = "ubuntu-data-partuuid"
  1940  
  1941  		// and also add the /run/mnt/host/ubuntu-data mountpoint for
  1942  		// cross-checking after it is mounted
  1943  		mockDiskMapping[disks.Mountpoint{Mountpoint: boot.InitramfsHostUbuntuDataDir}] = disk
  1944  	}
  1945  
  1946  	restore := disks.MockMountPointDisksToPartitionMapping(mockDiskMapping)
  1947  	defer restore()
  1948  
  1949  	measureEpochCalls := 0
  1950  	measureModelCalls := 0
  1951  	restore = main.MockSecbootMeasureSnapSystemEpochWhenPossible(func() error {
  1952  		measureEpochCalls++
  1953  		return nil
  1954  	})
  1955  	defer restore()
  1956  
  1957  	var measuredModel *asserts.Model
  1958  	restore = main.MockSecbootMeasureSnapModelWhenPossible(func(findModel func() (*asserts.Model, error)) error {
  1959  		measureModelCalls++
  1960  		var err error
  1961  		measuredModel, err = findModel()
  1962  		if err != nil {
  1963  			return err
  1964  		}
  1965  		return nil
  1966  	})
  1967  	defer restore()
  1968  
  1969  	restore = s.mockSystemdMountSequence(c, modeMnts, nil)
  1970  	defer restore()
  1971  
  1972  	if mode == "recover" {
  1973  		// use the helper
  1974  		s.testRecoverModeHappy(c)
  1975  	} else {
  1976  		_, err := main.Parser().ParseArgs([]string{"initramfs-mounts"})
  1977  		c.Assert(err, IsNil)
  1978  
  1979  		modeEnv := filepath.Join(boot.InitramfsDataDir, "/system-data/var/lib/snapd/modeenv")
  1980  		c.Check(modeEnv, testutil.FileEquals, `mode=install
  1981  recovery_system=20191118
  1982  `)
  1983  	}
  1984  
  1985  	c.Check(measuredModel, NotNil)
  1986  	c.Check(measuredModel, DeepEquals, s.model)
  1987  	c.Check(measureEpochCalls, Equals, 1)
  1988  	c.Check(measureModelCalls, Equals, 1)
  1989  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, "secboot-epoch-measured"), testutil.FilePresent)
  1990  	c.Assert(filepath.Join(dirs.SnapBootstrapRunDir, s.sysLabel+"-model-measured"), testutil.FilePresent)
  1991  }
  1992  
  1993  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeMeasure(c *C) {
  1994  	s.testInitramfsMountsInstallRecoverModeMeasure(c, "install")
  1995  }
  1996  
  1997  func (s *initramfsMountsSuite) TestInitramfsMountsInstallModeUnsetMeasure(c *C) {
  1998  	// TODO:UC20: eventually we should require snapd_recovery_mode to be set to
  1999  	// explicitly "install" for install mode, but we originally allowed
  2000  	// snapd_recovery_mode="" and interpreted it as install mode, so test that
  2001  	// case too
  2002  	s.testInitramfsMountsInstallRecoverModeMeasure(c, "")
  2003  }
  2004  
  2005  func (s *initramfsMountsSuite) TestInitramfsMountsRecoverModeMeasure(c *C) {
  2006  	s.testInitramfsMountsInstallRecoverModeMeasure(c, "recover")
  2007  }