summaryrefslogtreecommitdiff
diff options
-rw-r--r--boot/boot.go6
-rw-r--r--boot/kernel_os.go8
-rw-r--r--boot/kernel_os_test.go9
-rw-r--r--bootloader/androidboot.go18
-rw-r--r--bootloader/androidboot_test.go14
-rw-r--r--bootloader/bootloader.go36
-rw-r--r--bootloader/bootloader_test.go17
-rw-r--r--bootloader/export_test.go24
-rw-r--r--bootloader/grub.go18
-rw-r--r--bootloader/grub_test.go51
-rw-r--r--bootloader/uboot.go18
-rw-r--r--bootloader/uboot_test.go33
-rw-r--r--cmd/snap-confine/mount-support.c12
-rw-r--r--cmd/snap-update-ns/change.go12
-rw-r--r--cmd/snap-update-ns/change_test.go84
-rw-r--r--image/image.go27
-rw-r--r--image/image_test.go72
-rw-r--r--interfaces/apparmor/spec.go7
-rw-r--r--interfaces/apparmor/spec_test.go35
-rw-r--r--interfaces/apparmor/template.go1
-rw-r--r--interfaces/builtin/content.go2
-rw-r--r--interfaces/builtin/content_test.go56
-rw-r--r--interfaces/builtin/wayland.go1
-rw-r--r--interfaces/builtin/x11.go2
-rw-r--r--overlord/devicestate/firstboot.go210
-rw-r--r--overlord/devicestate/firstboot_test.go39
-rw-r--r--overlord/snapstate/handlers_link_test.go8
-rw-r--r--overlord/snapstate/snapstate_test.go93
-rw-r--r--seed/export_test.go24
-rw-r--r--seed/helpers.go36
-rw-r--r--seed/seed.go98
-rw-r--r--seed/seed16.go294
-rw-r--r--seed/seed16_test.go1058
-rw-r--r--seed/seed_yaml.go15
-rw-r--r--seed/seed_yaml_test.go4
-rw-r--r--seed/seedtest/seedtest.go1
-rwxr-xr-xtests/lib/bin/mountinfo-tool26
-rw-r--r--tests/lib/boot.sh7
-rw-r--r--tests/main/mount-ns/google.ubuntu-core-16-64/PER-SNAP-16.expected.txt306
-rw-r--r--tests/main/mount-ns/google.ubuntu-core-16-64/PER-USER-16.expected.txt306
-rw-r--r--tests/main/ubuntu-core-upgrade/task.yaml37
-rw-r--r--tests/main/ubuntu-core-writablepaths/task.yaml2
-rw-r--r--tests/regression/lp-1667385/task.yaml2
-rw-r--r--tests/regression/lp-1802581/task.yaml2
44 files changed, 2422 insertions, 709 deletions
diff --git a/boot/boot.go b/boot/boot.go
index b580dcd1cd..19b1b23a5f 100644
--- a/boot/boot.go
+++ b/boot/boot.go
@@ -135,7 +135,7 @@ func applicable(s snap.PlaceInfo, t snap.Type, model Model, onClassic bool) bool
// InUse checks if the given name/revision is used in the
// boot environment
func InUse(name string, rev snap.Revision) bool {
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find("", nil)
if err != nil {
logger.Noticef("cannot get boot settings: %s", err)
return false
@@ -182,7 +182,7 @@ func GetCurrentBoot(t snap.Type) (*NameAndRevision, error) {
return nil, fmt.Errorf("internal error: cannot find boot revision for snap type %q", t)
}
- bloader, err := bootloader.Find()
+ bloader, err := bootloader.Find("", nil)
if err != nil {
return nil, fmt.Errorf("cannot get boot settings: %s", err)
}
@@ -244,7 +244,7 @@ func nameAndRevnoFromSnap(sn string) (*NameAndRevision, error) {
// will set snap_mode="" and the system will boot with the known good
// values from snap_{core,kernel}
func MarkBootSuccessful() error {
- bl, err := bootloader.Find()
+ bl, err := bootloader.Find("", nil)
if err != nil {
return fmt.Errorf("cannot mark boot successful: %s", err)
}
diff --git a/boot/kernel_os.go b/boot/kernel_os.go
index 324cf052d0..41d552e80a 100644
--- a/boot/kernel_os.go
+++ b/boot/kernel_os.go
@@ -39,7 +39,7 @@ var _ BootParticipant = (*coreBootParticipant)(nil)
func (*coreBootParticipant) IsTrivial() bool { return false }
func (bs *coreBootParticipant) SetNextBoot() error {
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find("", nil)
if err != nil {
return fmt.Errorf("cannot set next boot: %s", err)
}
@@ -83,7 +83,7 @@ func (bs *coreBootParticipant) SetNextBoot() error {
}
func (bs *coreBootParticipant) ChangeRequiresReboot() bool {
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find("", nil)
if err != nil {
logger.Noticef("cannot get boot settings: %s", err)
return false
@@ -124,7 +124,7 @@ func (*coreKernel) IsTrivial() bool { return false }
func (k *coreKernel) RemoveKernelAssets() error {
// XXX: shouldn't we check the snap type?
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find("", nil)
if err != nil {
return fmt.Errorf("cannot remove kernel assets: %s", err)
}
@@ -134,7 +134,7 @@ func (k *coreKernel) RemoveKernelAssets() error {
}
func (k *coreKernel) ExtractKernelAssets(snapf snap.Container) error {
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find("", nil)
if err != nil {
return fmt.Errorf("cannot extract kernel assets: %s", err)
}
diff --git a/boot/kernel_os_test.go b/boot/kernel_os_test.go
index 25d15196f2..05160a405c 100644
--- a/boot/kernel_os_test.go
+++ b/boot/kernel_os_test.go
@@ -29,6 +29,7 @@ import (
"github.com/snapcore/snapd/boot"
"github.com/snapcore/snapd/bootloader"
"github.com/snapcore/snapd/bootloader/bootloadertest"
+ "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
@@ -223,10 +224,10 @@ func (s *ubootBootSetSuite) forceUbootBootloader(c *C) bootloader.Bootloader {
mockGadgetDir := c.MkDir()
err := ioutil.WriteFile(filepath.Join(mockGadgetDir, "uboot.conf"), nil, 0644)
c.Assert(err, IsNil)
- err = bootloader.InstallBootConfig(mockGadgetDir)
+ err = bootloader.InstallBootConfig(mockGadgetDir, dirs.GlobalRootDir)
c.Assert(err, IsNil)
- bloader, err := bootloader.Find()
+ bloader, err := bootloader.Find("", nil)
c.Assert(err, IsNil)
c.Check(bloader, NotNil)
bootloader.Force(bloader)
@@ -302,10 +303,10 @@ func (s *grubBootSetSuite) forceGrubBootloader(c *C) bootloader.Bootloader {
mockGadgetDir := c.MkDir()
err := ioutil.WriteFile(filepath.Join(mockGadgetDir, "grub.conf"), nil, 0644)
c.Assert(err, IsNil)
- err = bootloader.InstallBootConfig(mockGadgetDir)
+ err = bootloader.InstallBootConfig(mockGadgetDir, dirs.GlobalRootDir)
c.Assert(err, IsNil)
- bloader, err := bootloader.Find()
+ bloader, err := bootloader.Find("", nil)
c.Assert(err, IsNil)
c.Check(bloader, NotNil)
bloader.SetBootVars(map[string]string{
diff --git a/bootloader/androidboot.go b/bootloader/androidboot.go
index 952644618a..9ab8ddd91a 100644
--- a/bootloader/androidboot.go
+++ b/bootloader/androidboot.go
@@ -24,16 +24,17 @@ import (
"path/filepath"
"github.com/snapcore/snapd/bootloader/androidbootenv"
- "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
)
-type androidboot struct{}
+type androidboot struct {
+ rootdir string
+}
// newAndroidboot creates a new Androidboot bootloader object
-func newAndroidBoot() Bootloader {
- a := &androidboot{}
+func newAndroidBoot(rootdir string) Bootloader {
+ a := &androidboot{rootdir: rootdir}
if !osutil.FileExists(a.ConfigFile()) {
return nil
}
@@ -44,8 +45,15 @@ func (a *androidboot) Name() string {
return "androidboot"
}
+func (a *androidboot) setRootDir(rootdir string) {
+ a.rootdir = rootdir
+}
+
func (a *androidboot) dir() string {
- return filepath.Join(dirs.GlobalRootDir, "/boot/androidboot")
+ if a.rootdir == "" {
+ panic("internal error: unset rootdir")
+ }
+ return filepath.Join(a.rootdir, "/boot/androidboot")
}
func (a *androidboot) ConfigFile() string {
diff --git a/bootloader/androidboot_test.go b/bootloader/androidboot_test.go
index f983d541bb..8ebfb959c3 100644
--- a/bootloader/androidboot_test.go
+++ b/bootloader/androidboot_test.go
@@ -25,7 +25,6 @@ import (
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/bootloader"
- "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
@@ -41,22 +40,21 @@ func (s *androidBootTestSuite) SetUpTest(c *C) {
s.baseBootenvTestSuite.SetUpTest(c)
// the file needs to exist for androidboot object to be created
- bootloader.MockAndroidBootFile(c, 0644)
+ bootloader.MockAndroidBootFile(c, s.rootdir, 0644)
}
func (s *androidBootTestSuite) TestNewAndroidbootNoAndroidbootReturnsNil(c *C) {
- dirs.GlobalRootDir = "/something/not/there"
- a := bootloader.NewAndroidBoot()
+ a := bootloader.NewAndroidBoot("/something/not/there")
c.Assert(a, IsNil)
}
func (s *androidBootTestSuite) TestNewAndroidboot(c *C) {
- a := bootloader.NewAndroidBoot()
+ a := bootloader.NewAndroidBoot(s.rootdir)
c.Assert(a, NotNil)
}
func (s *androidBootTestSuite) TestSetGetBootVar(c *C) {
- a := bootloader.NewAndroidBoot()
+ a := bootloader.NewAndroidBoot(s.rootdir)
bootVars := map[string]string{"snap_mode": "try"}
a.SetBootVars(bootVars)
@@ -67,7 +65,7 @@ func (s *androidBootTestSuite) TestSetGetBootVar(c *C) {
}
func (s *androidBootTestSuite) TestExtractKernelAssetsNoUnpacksKernel(c *C) {
- a := bootloader.NewAndroidBoot()
+ a := bootloader.NewAndroidBoot(s.rootdir)
c.Assert(a, NotNil)
@@ -91,6 +89,6 @@ func (s *androidBootTestSuite) TestExtractKernelAssetsNoUnpacksKernel(c *C) {
c.Assert(err, IsNil)
// kernel is *not* here
- kernimg := filepath.Join(dirs.GlobalRootDir, "boot", "androidboot", "ubuntu-kernel_42.snap", "kernel.img")
+ kernimg := filepath.Join(s.rootdir, "boot", "androidboot", "ubuntu-kernel_42.snap", "kernel.img")
c.Assert(osutil.FileExists(kernimg), Equals, false)
}
diff --git a/bootloader/bootloader.go b/bootloader/bootloader.go
index 197db10105..c44f9a39ff 100644
--- a/bootloader/bootloader.go
+++ b/bootloader/bootloader.go
@@ -25,6 +25,7 @@ import (
"os"
"path/filepath"
+ "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
)
@@ -56,16 +57,23 @@ type Bootloader interface {
RemoveKernelAssets(s snap.PlaceInfo) error
}
+type installableBootloader interface {
+ Bootloader
+ setRootDir(string)
+}
+
// InstallBootConfig installs the bootloader config from the gadget
// snap dir into the right place.
-func InstallBootConfig(gadgetDir string) error {
- for _, bl := range []Bootloader{&grub{}, &uboot{}, &androidboot{}} {
+func InstallBootConfig(gadgetDir, rootDir string) error {
+ for _, bl := range []installableBootloader{&grub{}, &uboot{}, &androidboot{}} {
// the bootloader config file has to be root of the gadget snap
gadgetFile := filepath.Join(gadgetDir, bl.Name()+".conf")
if !osutil.FileExists(gadgetFile) {
continue
}
+ bl.setRootDir(rootDir)
+
systemFile := bl.ConfigFile()
if err := os.MkdirAll(filepath.Dir(systemFile), 0755); err != nil {
return err
@@ -81,25 +89,37 @@ var (
forcedError error
)
-// Find returns the bootloader for the given system
-// or an error if no bootloader is found
-func Find() (Bootloader, error) {
+// Options carries bootloader options.
+type Options struct {
+ // PrepareImageTime indicates whether the booloader is being
+ // used at prepare-image time, that means not on a runtime
+ // system.
+ PrepareImageTime bool
+}
+
+// Find returns the bootloader for the system
+// or an error if no bootloader is found.
+func Find(rootdir string, _ *Options) (Bootloader, error) {
if forcedBootloader != nil || forcedError != nil {
return forcedBootloader, forcedError
}
+ if rootdir == "" {
+ rootdir = dirs.GlobalRootDir
+ }
+
// try uboot
- if uboot := newUboot(); uboot != nil {
+ if uboot := newUboot(rootdir); uboot != nil {
return uboot, nil
}
// no, try grub
- if grub := newGrub(); grub != nil {
+ if grub := newGrub(rootdir); grub != nil {
return grub, nil
}
// no, try androidboot
- if androidboot := newAndroidBoot(); androidboot != nil {
+ if androidboot := newAndroidBoot(rootdir); androidboot != nil {
return androidboot, nil
}
diff --git a/bootloader/bootloader_test.go b/bootloader/bootloader_test.go
index bc603465da..65bd7900bd 100644
--- a/bootloader/bootloader_test.go
+++ b/bootloader/bootloader_test.go
@@ -29,7 +29,7 @@ import (
"github.com/snapcore/snapd/bootloader"
"github.com/snapcore/snapd/bootloader/bootloadertest"
- "github.com/snapcore/snapd/dirs"
+ //"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/testutil"
@@ -47,13 +47,14 @@ vendor: Someone
type baseBootenvTestSuite struct {
testutil.BaseTest
+
+ rootdir string
}
func (s *baseBootenvTestSuite) SetUpTest(c *C) {
s.BaseTest.SetUpTest(c)
s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
- dirs.SetRootDir(c.MkDir())
- s.AddCleanup(func() { dirs.SetRootDir("") })
+ s.rootdir = c.MkDir()
}
type bootenvTestSuite struct {
@@ -74,7 +75,7 @@ func (s *bootenvTestSuite) TestForceBootloader(c *C) {
bootloader.Force(s.b)
defer bootloader.Force(nil)
- got, err := bootloader.Find()
+ got, err := bootloader.Find("", nil)
c.Assert(err, IsNil)
c.Check(got, Equals, s.b)
}
@@ -84,13 +85,13 @@ func (s *bootenvTestSuite) TestForceBootloaderError(c *C) {
bootloader.ForceError(myErr)
defer bootloader.ForceError(nil)
- got, err := bootloader.Find()
+ got, err := bootloader.Find("", nil)
c.Assert(err, Equals, myErr)
c.Check(got, IsNil)
}
func (s *bootenvTestSuite) TestInstallBootloaderConfigNoConfig(c *C) {
- err := bootloader.InstallBootConfig(c.MkDir())
+ err := bootloader.InstallBootConfig(c.MkDir(), s.rootdir)
c.Assert(err, ErrorMatches, `cannot find boot config in.*`)
}
@@ -103,9 +104,9 @@ func (s *bootenvTestSuite) TestInstallBootloaderConfig(c *C) {
mockGadgetDir := c.MkDir()
err := ioutil.WriteFile(filepath.Join(mockGadgetDir, t.gadgetFile), nil, 0644)
c.Assert(err, IsNil)
- err = bootloader.InstallBootConfig(mockGadgetDir)
+ err = bootloader.InstallBootConfig(mockGadgetDir, s.rootdir)
c.Assert(err, IsNil)
- fn := filepath.Join(dirs.GlobalRootDir, t.systemFile)
+ fn := filepath.Join(s.rootdir, t.systemFile)
c.Assert(osutil.FileExists(fn), Equals, true)
}
}
diff --git a/bootloader/export_test.go b/bootloader/export_test.go
index 67dbd2ef53..2433a359b9 100644
--- a/bootloader/export_test.go
+++ b/bootloader/export_test.go
@@ -29,24 +29,24 @@ import (
)
// creates a new Androidboot bootloader object
-func NewAndroidBoot() Bootloader {
- return newAndroidBoot()
+func NewAndroidBoot(rootdir string) Bootloader {
+ return newAndroidBoot(rootdir)
}
-func MockAndroidBootFile(c *C, mode os.FileMode) {
- f := &androidboot{}
+func MockAndroidBootFile(c *C, rootdir string, mode os.FileMode) {
+ f := &androidboot{rootdir: rootdir}
err := os.MkdirAll(f.dir(), 0755)
c.Assert(err, IsNil)
err = ioutil.WriteFile(f.ConfigFile(), nil, mode)
c.Assert(err, IsNil)
}
-func NewUboot() Bootloader {
- return newUboot()
+func NewUboot(rootdir string) Bootloader {
+ return newUboot(rootdir)
}
-func MockUbootFiles(c *C) {
- u := &uboot{}
+func MockUbootFiles(c *C, rootdir string) {
+ u := &uboot{rootdir: rootdir}
err := os.MkdirAll(u.dir(), 0755)
c.Assert(err, IsNil)
@@ -57,12 +57,12 @@ func MockUbootFiles(c *C) {
c.Assert(err, IsNil)
}
-func NewGrub() Bootloader {
- return newGrub()
+func NewGrub(rootdir string) Bootloader {
+ return newGrub(rootdir)
}
-func MockGrubFiles(c *C) {
- g := &grub{}
+func MockGrubFiles(c *C, rootdir string) {
+ g := &grub{rootdir: rootdir}
err := os.MkdirAll(g.dir(), 0755)
c.Assert(err, IsNil)
err = ioutil.WriteFile(g.ConfigFile(), nil, 0644)
diff --git a/bootloader/grub.go b/bootloader/grub.go
index 2364b61ae4..fe7999b152 100644
--- a/bootloader/grub.go
+++ b/bootloader/grub.go
@@ -24,16 +24,17 @@ import (
"path/filepath"
"github.com/snapcore/snapd/bootloader/grubenv"
- "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
)
-type grub struct{}
+type grub struct {
+ rootdir string
+}
// newGrub create a new Grub bootloader object
-func newGrub() Bootloader {
- g := &grub{}
+func newGrub(rootdir string) Bootloader {
+ g := &grub{rootdir: rootdir}
if !osutil.FileExists(g.ConfigFile()) {
return nil
}
@@ -45,8 +46,15 @@ func (g *grub) Name() string {
return "grub"
}
+func (g *grub) setRootDir(rootdir string) {
+ g.rootdir = rootdir
+}
+
func (g *grub) dir() string {
- return filepath.Join(dirs.GlobalRootDir, "/boot/grub")
+ if g.rootdir == "" {
+ panic("internal error: unset rootdir")
+ }
+ return filepath.Join(g.rootdir, "/boot/grub")
}
func (g *grub) ConfigFile() string {
diff --git a/bootloader/grub_test.go b/bootloader/grub_test.go
index 96a207f2a1..562affdd02 100644
--- a/bootloader/grub_test.go
+++ b/bootloader/grub_test.go
@@ -44,9 +44,9 @@ var _ = Suite(&grubTestSuite{})
func (s *grubTestSuite) SetUpTest(c *C) {
s.baseBootenvTestSuite.SetUpTest(c)
- bootloader.MockGrubFiles(c)
+ bootloader.MockGrubFiles(c, s.rootdir)
- s.bootdir = filepath.Join(dirs.GlobalRootDir, "boot")
+ s.bootdir = filepath.Join(s.rootdir, "boot")
}
// grubEditenvCmd finds the right grub{,2}-editenv command
@@ -59,25 +59,25 @@ func grubEditenvCmd() string {
return ""
}
-func grubEnvPath() string {
- return filepath.Join(dirs.GlobalRootDir, "boot/grub/grubenv")
+func grubEnvPath(rootdir string) string {
+ return filepath.Join(rootdir, "boot/grub/grubenv")
}
-func grubEditenvSet(c *C, key, value string) {
+func (s *grubTestSuite) grubEditenvSet(c *C, key, value string) {
if grubEditenvCmd() == "" {
c.Skip("grub{,2}-editenv is not available")
}
- err := exec.Command(grubEditenvCmd(), grubEnvPath(), "set", fmt.Sprintf("%s=%s", key, value)).Run()
+ err := exec.Command(grubEditenvCmd(), grubEnvPath(s.rootdir), "set", fmt.Sprintf("%s=%s", key, value)).Run()
c.Assert(err, IsNil)
}
-func grubEditenvGet(c *C, key string) string {
+func (s *grubTestSuite) grubEditenvGet(c *C, key string) string {
if grubEditenvCmd() == "" {
c.Skip("grub{,2}-editenv is not available")
}
- output, err := exec.Command(grubEditenvCmd(), grubEnvPath(), "list").CombinedOutput()
+ output, err := exec.Command(grubEditenvCmd(), grubEnvPath(s.rootdir), "list").CombinedOutput()
c.Assert(err, IsNil)
cfg := goconfigparser.New()
cfg.AllowNoSectionHeader = true
@@ -89,20 +89,18 @@ func grubEditenvGet(c *C, key string) string {
}
func (s *grubTestSuite) makeFakeGrubEnv(c *C) {
- grubEditenvSet(c, "k", "v")
+ s.grubEditenvSet(c, "k", "v")
}
func (s *grubTestSuite) TestNewGrubNoGrubReturnsNil(c *C) {
- dirs.GlobalRootDir = "/something/not/there"
-
- g := bootloader.NewGrub()
+ g := bootloader.NewGrub("/something/not/there")
c.Assert(g, IsNil)
}
func (s *grubTestSuite) TestNewGrub(c *C) {
s.makeFakeGrubEnv(c)
- g := bootloader.NewGrub()
+ g := bootloader.NewGrub(s.rootdir)
c.Assert(g, NotNil)
c.Assert(g.Name(), Equals, "grub")
}
@@ -110,16 +108,27 @@ func (s *grubTestSuite) TestNewGrub(c *C) {
func (s *grubTestSuite) TestGetBootloaderWithGrub(c *C) {
s.makeFakeGrubEnv(c)
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find(s.rootdir, nil)
+ c.Assert(err, IsNil)
+ c.Assert(bootloader.Name(), Equals, "grub")
+}
+
+func (s *grubTestSuite) TestGetBootloaderWithGrubWithDefaultRoot(c *C) {
+ s.makeFakeGrubEnv(c)
+
+ dirs.SetRootDir(s.rootdir)
+ defer func() { dirs.SetRootDir("") }()
+
+ bootloader, err := bootloader.Find("", nil)
c.Assert(err, IsNil)
c.Assert(bootloader.Name(), Equals, "grub")
}
func (s *grubTestSuite) TestGetBootVer(c *C) {
s.makeFakeGrubEnv(c)
- grubEditenvSet(c, "snap_mode", "regular")
+ s.grubEditenvSet(c, "snap_mode", "regular")
- g := bootloader.NewGrub()
+ g := bootloader.NewGrub(s.rootdir)
v, err := g.GetBootVars("snap_mode")
c.Assert(err, IsNil)
c.Check(v, HasLen, 1)
@@ -129,21 +138,21 @@ func (s *grubTestSuite) TestGetBootVer(c *C) {
func (s *grubTestSuite) TestSetBootVer(c *C) {
s.makeFakeGrubEnv(c)
- g := bootloader.NewGrub()
+ g := bootloader.NewGrub(s.rootdir)
err := g.SetBootVars(map[string]string{
"k1": "v1",
"k2": "v2",
})
c.Assert(err, IsNil)
- c.Check(grubEditenvGet(c, "k1"), Equals, "v1")
- c.Check(grubEditenvGet(c, "k2"), Equals, "v2")
+ c.Check(s.grubEditenvGet(c, "k1"), Equals, "v1")
+ c.Check(s.grubEditenvGet(c, "k2"), Equals, "v2")
}
func (s *grubTestSuite) TestExtractKernelAssetsNoUnpacksKernelForGrub(c *C) {
s.makeFakeGrubEnv(c)
- g := bootloader.NewGrub()
+ g := bootloader.NewGrub(s.rootdir)
files := [][]string{
{"kernel.img", "I'm a kernel"},
@@ -172,7 +181,7 @@ func (s *grubTestSuite) TestExtractKernelAssetsNoUnpacksKernelForGrub(c *C) {
func (s *grubTestSuite) TestExtractKernelForceWorks(c *C) {
s.makeFakeGrubEnv(c)
- g := bootloader.NewGrub()
+ g := bootloader.NewGrub(s.rootdir)
c.Assert(g, NotNil)
files := [][]string{
diff --git a/bootloader/uboot.go b/bootloader/uboot.go
index f460b47395..0a475fcfdb 100644
--- a/bootloader/uboot.go
+++ b/bootloader/uboot.go
@@ -23,16 +23,17 @@ import (
"path/filepath"
"github.com/snapcore/snapd/bootloader/ubootenv"
- "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
)
-type uboot struct{}
+type uboot struct {
+ rootdir string
+}
// newUboot create a new Uboot bootloader object
-func newUboot() Bootloader {
- u := &uboot{}
+func newUboot(rootdir string) Bootloader {
+ u := &uboot{rootdir: rootdir}
if !osutil.FileExists(u.envFile()) {
return nil
}
@@ -44,8 +45,15 @@ func (u *uboot) Name() string {
return "uboot"
}
+func (u *uboot) setRootDir(rootdir string) {
+ u.rootdir = rootdir
+}
+
func (u *uboot) dir() string {
- return filepath.Join(dirs.GlobalRootDir, "/boot/uboot")
+ if u.rootdir == "" {
+ panic("internal error: unset rootdir")
+ }
+ return filepath.Join(u.rootdir, "/boot/uboot")
}
func (u *uboot) ConfigFile() string {
diff --git a/bootloader/uboot_test.go b/bootloader/uboot_test.go
index a85f487cb6..8276a231ae 100644
--- a/bootloader/uboot_test.go
+++ b/bootloader/uboot_test.go
@@ -28,7 +28,6 @@ import (
"github.com/snapcore/snapd/bootloader"
"github.com/snapcore/snapd/bootloader/ubootenv"
- "github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
@@ -42,20 +41,20 @@ type ubootTestSuite struct {
var _ = Suite(&ubootTestSuite{})
func (s *ubootTestSuite) TestNewUbootNoUbootReturnsNil(c *C) {
- u := bootloader.NewUboot()
+ u := bootloader.NewUboot(s.rootdir)
c.Assert(u, IsNil)
}
func (s *ubootTestSuite) TestNewUboot(c *C) {
- bootloader.MockUbootFiles(c)
- u := bootloader.NewUboot()
+ bootloader.MockUbootFiles(c, s.rootdir)
+ u := bootloader.NewUboot(s.rootdir)
c.Assert(u, NotNil)
c.Assert(u.Name(), Equals, "uboot")
}
func (s *ubootTestSuite) TestUbootGetEnvVar(c *C) {
- bootloader.MockUbootFiles(c)
- u := bootloader.NewUboot()
+ bootloader.MockUbootFiles(c, s.rootdir)
+ u := bootloader.NewUboot(s.rootdir)
c.Assert(u, NotNil)
err := u.SetBootVars(map[string]string{
"snap_mode": "",
@@ -72,16 +71,16 @@ func (s *ubootTestSuite) TestUbootGetEnvVar(c *C) {
}
func (s *ubootTestSuite) TestGetBootloaderWithUboot(c *C) {
- bootloader.MockUbootFiles(c)
+ bootloader.MockUbootFiles(c, s.rootdir)
- bootloader, err := bootloader.Find()
+ bootloader, err := bootloader.Find(s.rootdir, nil)
c.Assert(err, IsNil)
c.Assert(bootloader.Name(), Equals, "uboot")
}
func (s *ubootTestSuite) TestUbootSetEnvNoUselessWrites(c *C) {
- bootloader.MockUbootFiles(c)
- u := bootloader.NewUboot()
+ bootloader.MockUbootFiles(c, s.rootdir)
+ u := bootloader.NewUboot(s.rootdir)
c.Assert(u, NotNil)
envFile := u.ConfigFile()
@@ -110,8 +109,8 @@ func (s *ubootTestSuite) TestUbootSetEnvNoUselessWrites(c *C) {
}
func (s *ubootTestSuite) TestUbootSetBootVarFwEnv(c *C) {
- bootloader.MockUbootFiles(c)
- u := bootloader.NewUboot()
+ bootloader.MockUbootFiles(c, s.rootdir)
+ u := bootloader.NewUboot(s.rootdir)
err := u.SetBootVars(map[string]string{"key": "value"})
c.Assert(err, IsNil)
@@ -122,8 +121,8 @@ func (s *ubootTestSuite) TestUbootSetBootVarFwEnv(c *C) {
}
func (s *ubootTestSuite) TestUbootGetBootVarFwEnv(c *C) {
- bootloader.MockUbootFiles(c)
- u := bootloader.NewUboot()
+ bootloader.MockUbootFiles(c, s.rootdir)
+ u := bootloader.NewUboot(s.rootdir)
err := u.SetBootVars(map[string]string{"key2": "value2"})
c.Assert(err, IsNil)
@@ -134,8 +133,8 @@ func (s *ubootTestSuite) TestUbootGetBootVarFwEnv(c *C) {
}
func (s *ubootTestSuite) TestExtractKernelAssetsAndRemove(c *C) {
- bootloader.MockUbootFiles(c)
- u := bootloader.NewUboot()
+ bootloader.MockUbootFiles(c, s.rootdir)
+ u := bootloader.NewUboot(s.rootdir)
files := [][]string{
{"kernel.img", "I'm a kernel"},
@@ -160,7 +159,7 @@ func (s *ubootTestSuite) TestExtractKernelAssetsAndRemove(c *C) {
c.Assert(err, IsNil)
// this is where the kernel/initrd is unpacked
- kernelAssetsDir := filepath.Join(dirs.GlobalRootDir, "boot", "uboot", "ubuntu-kernel_42.snap")
+ kernelAssetsDir := filepath.Join(s.rootdir, "boot", "uboot", "ubuntu-kernel_42.snap")
for _, def := range files {
if def[0] == "meta/kernel.yaml" {
diff --git a/cmd/snap-confine/mount-support.c b/cmd/snap-confine/mount-support.c
index 5a58323e8e..411fbd4598 100644
--- a/cmd/snap-confine/mount-support.c
+++ b/cmd/snap-confine/mount-support.c
@@ -406,10 +406,14 @@ static void sc_bootstrap_mount_namespace(const struct sc_mount_config *config)
// the this directory on the host filesystem may not match the location in
// the desired root filesystem. In the "core" and "ubuntu-core" snaps the
// directory is always /snap. On the host it is a build-time configuration
- // option stored in SNAP_MOUNT_DIR.
- sc_must_snprintf(dst, sizeof dst, "%s/snap", scratch_dir);
- sc_do_mount(SNAP_MOUNT_DIR, dst, NULL, MS_BIND | MS_REC, NULL);
- sc_do_mount("none", dst, NULL, MS_REC | MS_SLAVE, NULL);
+ // option stored in SNAP_MOUNT_DIR. In legacy mode (or in other words, not
+ // in normal mode), we don't need to do this because /snap is fixed and
+ // already contains the correct view of the mounted snaps.
+ if (config->normal_mode) {
+ sc_must_snprintf(dst, sizeof dst, "%s/snap", scratch_dir);
+ sc_do_mount(SNAP_MOUNT_DIR, dst, NULL, MS_BIND | MS_REC, NULL);
+ sc_do_mount("none", dst, NULL, MS_REC | MS_SLAVE, NULL);
+ }
// Create the hostfs directory if one is missing. This directory is a part
// of packaging now so perhaps this code can be removed later.
if (access(SC_HOSTFS_DIR, F_OK) != 0) {
diff --git a/cmd/snap-update-ns/change.go b/cmd/snap-update-ns/change.go
index 7fd76aa4a2..faddf388be 100644
--- a/cmd/snap-update-ns/change.go
+++ b/cmd/snap-update-ns/change.go
@@ -330,10 +330,20 @@ func (c *Change) lowLevelPerform(as *Assumptions) error {
flags := umountNoFollow
if c.Entry.XSnapdDetach() {
flags |= syscall.MNT_DETACH
+ // If we are detaching something then before performing the actual detach
+ // switch the entire hierarchy to private event propagation (that is,
+ // none). This works around a bit of peculiar kernel behavior when the
+ // kernel reports EBUSY during a detach operation, because the changes
+ // propagate in a way that conflicts with itself. This is also documented
+ // in umount(2).
+ err = sysMount("none", c.Entry.Dir, "", syscall.MS_REC|syscall.MS_PRIVATE, "")
+ logger.Debugf("mount --make-rprivate %q (error: %v)", c.Entry.Dir, err)
}
// Perform the raw unmount operation.
- err = sysUnmount(c.Entry.Dir, flags)
+ if err == nil {
+ err = sysUnmount(c.Entry.Dir, flags)
+ }
if err == nil {
as.AddChange(c)
}
diff --git a/cmd/snap-update-ns/change_test.go b/cmd/snap-update-ns/change_test.go
index bf97814afe..3c4a0623b0 100644
--- a/cmd/snap-update-ns/change_test.go
+++ b/cmd/snap-update-ns/change_test.go
@@ -642,6 +642,7 @@ func (s *changeSuite) TestPerformFilesystemMountWithoutMountPointAndReadOnlyBase
{C: `lstat "/rofs"`, R: testutil.FileInfoDir},
{C: `mount "tmpfs" "/rofs" "tmpfs" 0 "mode=0755,uid=0,gid=0"`},
+ {C: `mount "none" "/tmp/.snap/rofs" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/tmp/.snap/rofs" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -805,6 +806,7 @@ func (s *changeSuite) TestPerformFilesystemDetch(c *C) {
synth, err := chg.Perform(s.as)
c.Assert(err, IsNil)
c.Assert(s.sys.RCalls(), testutil.SyscallsEqual, []testutil.CallResultError{
+ {C: `mount "none" "/target" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/target" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -1151,6 +1153,7 @@ func (s *changeSuite) TestPerformDirectoryBindMountWithoutMountPointAndReadOnlyB
{C: `lstat "/rofs"`, R: testutil.FileInfoDir},
{C: `mount "tmpfs" "/rofs" "tmpfs" 0 "mode=0755,uid=0,gid=0"`},
+ {C: `mount "none" "/tmp/.snap/rofs" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/tmp/.snap/rofs" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -1298,6 +1301,7 @@ func (s *changeSuite) TestPerformDirectoryBindMountWithoutMountSourceAndReadOnly
{C: `close 4`},
{C: `lstat "/rofs"`, R: testutil.FileInfoDir},
{C: `mount "tmpfs" "/rofs" "tmpfs" 0 "mode=0755,uid=0,gid=0"`},
+ {C: `mount "none" "/tmp/.snap/rofs" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/tmp/.snap/rofs" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -1690,6 +1694,7 @@ func (s *changeSuite) TestPerformFileBindMountWithoutMountPointAndReadOnlyBase(c
{C: `lstat "/rofs"`, R: testutil.FileInfoDir},
{C: `mount "tmpfs" "/rofs" "tmpfs" 0 "mode=0755,uid=0,gid=0"`},
+ {C: `mount "none" "/tmp/.snap/rofs" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/tmp/.snap/rofs" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -2082,6 +2087,7 @@ func (s *changeSuite) TestPerformCreateSymlinkWithoutBaseDirAndReadOnlyBase(c *C
{C: `lstat "/rofs"`, R: testutil.FileInfoDir},
{C: `mount "tmpfs" "/rofs" "tmpfs" 0 "mode=0755,uid=0,gid=0"`},
+ {C: `mount "none" "/tmp/.snap/rofs" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/tmp/.snap/rofs" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -2340,6 +2346,7 @@ func (s *changeSuite) TestPerformCreateSymlinkWithAvoidedTrespassing(c *C) {
{C: `close 7`},
// We're done restoring now.
+ {C: `mount "none" "/tmp/.snap/etc" "" MS_REC|MS_PRIVATE ""`},
{C: `unmount "/tmp/.snap/etc" UMOUNT_NOFOLLOW|MNT_DETACH`},
// Perform clean up after the unmount operation.
@@ -2424,3 +2431,80 @@ func (s *changeSuite) TestPerformedChangesAreTracked(c *C) {
{Action: update.Keep, Entry: osutil.MountEntry{Name: "tmpfs", Dir: "/target", Type: "tmpfs"}},
})
}
+
+func (s *changeSuite) TestComplexPropagatingChanges(c *C) {
+ // This problem is more subtle. It is a variant of the regression test
+ // implemented in tests/regression/lp-1831010. Here, we have four directories:
+ //
+ // - $SNAP/a
+ // - $SNAP/b
+ // - $SNAP/b/c
+ // - $SNAP/d
+ //
+ // but snapd's mount profile contains only two entries:
+ //
+ // 1) recursive-bind $SNAP/a -> $SNAP/b/c (ie, mount --rbind $SNAP/a $SNAP/b/c)
+ // 2) recursive-bind $SNAP/b -> $SNAP/d (ie, mount --rbind $SNAP/b $SNAP/d)
+ //
+ // Both mount operations are performed under a substrate that is MS_SHARED.
+ // Therefore, due to the rules that decide upon propagation of bind mounts
+ // the propagation of the new mount entries is also shared. This is
+ // documented in section 5b of
+ // https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt.
+ //
+ // Interactive experimentation shows that the following three mount points exist
+ // after this operation, as illustrated by findmnt:
+ //
+ // TARGET SOURCE FSTYPE OPTIONS
+ // ...
+ // └─/snap/test-snapd-layout/x1 /dev/loop1 squashfs ro,nodev,relatime
+ // ├─/snap/test-snapd-layout/x1/b/c /dev/loop1[/a] squashfs ro,nodev,relatime
+ // └─/snap/test-snapd-layout/x1/d /dev/loop1[/b] squashfs ro,nodev,relatime
+ // └─/snap/test-snapd-layout/x1/d/c /dev/loop1[/a] squashfs ro,nodev,relatime
+ //
+ // Note that after the first mount operation only one mount point is created, namely
+ // $SNAP/a -> $SNAP/b/c. The second recursive bind mount not only creates
+ // $SNAP/b -> $SNAP/d, but also replicates $SNAP/a -> $SNAP/b/c as
+ // $SNAP/a -> $SNAP/d/c.
+ //
+ // The test will simulate a refresh of the snap from revision x1 to revision
+ // x2. When this happens the mount profile associated with x1 must be undone
+ // and the mount profile associated with x2 must be constructed. Because
+ // ordering matters, let's first consider the order of construction of x1
+ // itself. Starting from nothing, apply x1 as follows:
+ x1 := &osutil.MountProfile{
+ Entries: []osutil.MountEntry{
+ {Name: "/snap/app/x1/a", Dir: "/snap/app/x1/b/c", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}},
+ {Name: "/snap/app/x1/b", Dir: "/snap/app/x1/d", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}},
+ },
+ }
+ changes := update.NeededChanges(&osutil.MountProfile{}, x1)
+ c.Assert(changes, DeepEquals, []*update.Change{
+ {Action: update.Mount, Entry: osutil.MountEntry{Name: "/snap/app/x1/a", Dir: "/snap/app/x1/b/c", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}}},
+ {Action: update.Mount, Entry: osutil.MountEntry{Name: "/snap/app/x1/b", Dir: "/snap/app/x1/d", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}}},
+ })
+ // We can see that x1 is constructed in alphabetical order, first recursively
+ // bind mount at $SNAP/a the directory $SNAP/b/c, second recursively bind
+ // mount at $SNAP/b the directory $SNAP/d.
+ x2 := &osutil.MountProfile{
+ Entries: []osutil.MountEntry{
+ {Name: "/snap/app/x2/a", Dir: "/snap/app/x2/b/c", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}},
+ {Name: "/snap/app/x2/b", Dir: "/snap/app/x2/d", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}},
+ },
+ }
+ // When we are asked to refresh to revision x2, using the same layout, we
+ // simply undo x1 and then create x2, which apart from the difference in
+ // revision name, is exactly the same. The undo code, however, does not take
+ // the replicated mount point under consideration and therefore attempts to
+ // detach "x1/d", which normally fails with EBUSY. To counter this, the
+ // unmount operation first switches the mount point to recursive private
+ // propagation, before actually unmounting it. This ensures that propagation
+ // doesn't self-conflict, simply because there isn't any left.
+ changes = update.NeededChanges(x1, x2)
+ c.Assert(changes, DeepEquals, []*update.Change{
+ {Action: update.Unmount, Entry: osutil.MountEntry{Name: "/snap/app/x1/b", Dir: "/snap/app/x1/d", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout", "x-snapd.detach"}}},
+ {Action: update.Unmount, Entry: osutil.MountEntry{Name: "/snap/app/x1/a", Dir: "/snap/app/x1/b/c", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout", "x-snapd.detach"}}},
+ {Action: update.Mount, Entry: osutil.MountEntry{Name: "/snap/app/x2/a", Dir: "/snap/app/x2/b/c", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}}},
+ {Action: update.Mount, Entry: osutil.MountEntry{Name: "/snap/app/x2/b", Dir: "/snap/app/x2/d", Type: "none", Options: []string{"rbind", "rw", "x-snapd.origin=layout"}}},
+ })
+}
diff --git a/image/image.go b/image/image.go
index 2a4e4ccadd..4da7983689 100644
--- a/image/image.go
+++ b/image/image.go
@@ -32,7 +32,6 @@ import (
"github.com/snapcore/snapd/asserts"
"github.com/snapcore/snapd/asserts/snapasserts"
"github.com/snapcore/snapd/asserts/sysdb"
- "github.com/snapcore/snapd/boot"
"github.com/snapcore/snapd/bootloader"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/osutil"
@@ -587,8 +586,10 @@ func setupSeed(tsto *ToolingStore, model *asserts.Model, opts *Options, local *l
}
if !opts.Classic {
+ // TODO: make this functionality part of the boot package?
+
// now do the bootloader stuff
- if err := bootloader.InstallBootConfig(opts.GadgetUnpackDir); err != nil {
+ if err := bootloader.InstallBootConfig(opts.GadgetUnpackDir, dirs.GlobalRootDir); err != nil {
return err
}
@@ -606,7 +607,7 @@ func setupSeed(tsto *ToolingStore, model *asserts.Model, opts *Options, local *l
}
type seedEntry struct {
- snap *seed.Snap
+ snap *seed.Snap16
snapType snap.Type
}
@@ -737,7 +738,7 @@ func (s *imageSeed) add(snapName string) error {
}
s.entries = append(s.entries, seedEntry{
- snap: &seed.Snap{
+ snap: &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID, // cross-ref
Channel: snapChannel,
@@ -791,12 +792,12 @@ func (s *imageSeed) checkBase(info *snap.Info) error {
return fmt.Errorf("cannot add snap %q without also adding its base %q explicitly", info.InstanceName(), info.Base)
}
-func (s *imageSeed) seedYaml() *seed.Seed {
- var seedYaml seed.Seed
+func (s *imageSeed) seedYaml() *seed.Seed16 {
+ var seedYaml seed.Seed16
sort.Stable(s.entries)
- seedYaml.Snaps = make([]*seed.Snap, len(s.entries))
+ seedYaml.Snaps = make([]*seed.Snap16, len(s.entries))
for i, e := range s.entries {
seedYaml.Snaps[i] = e.snap
}
@@ -812,7 +813,9 @@ func setBootvars(downloadedSnapsInfoForBootConfig map[string]*snap.Info, model *
// Set bootvars for kernel/core snaps so the system boots and
// does the first-time initialization. There is also no
// mounted kernel/core/base snap, but just the blobs.
- bloader, err := bootloader.Find()
+ bloader, err := bootloader.Find("", &bootloader.Options{
+ PrepareImageTime: true,
+ })
if err != nil {
return fmt.Errorf("cannot set kernel/core boot variables: %s", err)
}
@@ -849,7 +852,7 @@ func setBootvars(downloadedSnapsInfoForBootConfig map[string]*snap.Info, model *
bootvar = "snap_core"
case snap.TypeKernel:
bootvar = "snap_kernel"
- if err := extractKernelAssets(fn, info, model); err != nil {
+ if err := extractKernelAssets(bloader, fn, info, model); err != nil {
return err
}
}
@@ -866,15 +869,13 @@ func setBootvars(downloadedSnapsInfoForBootConfig map[string]*snap.Info, model *
return nil
}
-func extractKernelAssets(snapPath string, info *snap.Info, model *asserts.Model) error {
+func extractKernelAssets(bootloader bootloader.Bootloader, snapPath string, info *snap.Info, model *asserts.Model) error {
snapf, err := snap.Open(snapPath)
if err != nil {
return err
}
- // image always runs in not-on-classic mode
- kernel := boot.Kernel(info, info.GetType(), model, false)
- return kernel.ExtractKernelAssets(snapf)
+ return bootloader.ExtractKernelAssets(info, snapf)
}
func copyLocalSnapFile(snapPath, targetDir string, info *snap.Info) (dstPath string, err error) {
diff --git a/image/image_test.go b/image/image_test.go
index e1136b7515..a421c9dae8 100644
--- a/image/image_test.go
+++ b/image/image_test.go
@@ -610,7 +610,7 @@ func (s *imageSuite) TestSetupSeed(c *C) {
p := filepath.Join(seedsnapsdir, fn)
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: name,
SnapID: s.AssertedSnapID(name),
File: fn,
@@ -718,7 +718,7 @@ func (s *imageSuite) TestSetupSeedLocalCoreBrandKernel(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
File: fn,
@@ -793,25 +793,25 @@ func (s *imageSuite) TestSetupSeedDevmodeSnap(c *C) {
c.Assert(err, IsNil)
c.Check(seedYaml.Snaps, HasLen, 5)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "core",
SnapID: s.AssertedSnapID("core"),
File: "core_3.snap",
Channel: "beta",
})
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
SnapID: s.AssertedSnapID("pc-kernel"),
File: "pc-kernel_2.snap",
Channel: "beta",
})
- c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap16{
Name: "pc",
SnapID: s.AssertedSnapID("pc"),
File: "pc_1.snap",
Channel: "beta",
})
- c.Check(seedYaml.Snaps[3], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[3], DeepEquals, &seed.Snap16{
Name: "required-snap1",
SnapID: s.AssertedSnapID("required-snap1"),
File: "required-snap1_3.snap",
@@ -819,7 +819,7 @@ func (s *imageSuite) TestSetupSeedDevmodeSnap(c *C) {
Channel: "beta",
})
// ensure local snaps are put last in seed.yaml
- c.Check(seedYaml.Snaps[4], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[4], DeepEquals, &seed.Snap16{
Name: "devmode-snap",
DevMode: true,
Unasserted: true,
@@ -841,7 +841,7 @@ func (s *imageSuite) TestSetupSeedDevmodeSnap(c *C) {
// ensure local snaps are put last in seed.yaml
last := len(seedYaml.Snaps) - 1
- c.Check(seedYaml.Snaps[last], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[last], DeepEquals, &seed.Snap16{
Name: "devmode-snap",
File: fn,
DevMode: true,
@@ -936,7 +936,7 @@ func (s *imageSuite) TestSetupSeedWithBase(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
File: fn,
@@ -1020,7 +1020,7 @@ func (s *imageSuite) TestSetupSeedWithBaseLegacySnap(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
File: fn,
@@ -1190,7 +1190,7 @@ func (s *imageSuite) TestSetupSeedLocalSnapsWithStoreAsserts(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true, Commentf("cannot find %s", p))
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
File: fn,
@@ -1299,7 +1299,7 @@ func (s *imageSuite) TestSetupSeedLocalSnapsWithChannels(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true, Commentf("cannot find %s", p))
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
Channel: info.Channel,
@@ -1454,18 +1454,18 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) {
c.Assert(err, IsNil)
c.Check(seedYaml.Snaps, HasLen, 3)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "core",
SnapID: s.AssertedSnapID("core"),
File: "core_3.snap",
})
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
SnapID: s.AssertedSnapID("pc-kernel"),
File: "pc-kernel_2.snap",
Channel: "18/stable",
})
- c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap16{
Name: "pc",
SnapID: s.AssertedSnapID("pc"),
File: "pc_1.snap",
@@ -1508,19 +1508,19 @@ func (s *imageSuite) TestSetupSeedWithKernelTrackWithDefaultChannel(c *C) {
c.Assert(err, IsNil)
c.Check(seedYaml.Snaps, HasLen, 3)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "core",
SnapID: s.AssertedSnapID("core"),
File: "core_3.snap",
Channel: "edge",
})
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
SnapID: s.AssertedSnapID("pc-kernel"),
File: "pc-kernel_2.snap",
Channel: "18/edge",
})
- c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap16{
Name: "pc",
SnapID: s.AssertedSnapID("pc"),
File: "pc_1.snap",
@@ -1568,13 +1568,13 @@ func (s *imageSuite) TestSetupSeedWithKernelTrackOnLocalSnap(c *C) {
c.Assert(err, IsNil)
c.Check(seedYaml.Snaps, HasLen, 3)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "core",
SnapID: s.AssertedSnapID("core"),
File: "core_3.snap",
Channel: "beta",
})
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
SnapID: s.AssertedSnapID("pc-kernel"),
File: "pc-kernel_2.snap",
@@ -1622,32 +1622,32 @@ func (s *imageSuite) TestSetupSeedWithBaseAndLocalLegacyCoreOrdering(c *C) {
c.Assert(err, IsNil)
c.Check(seedYaml.Snaps, HasLen, 6)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "snapd",
SnapID: s.AssertedSnapID("snapd"),
File: "snapd_18.snap",
})
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "core",
Unasserted: true,
File: "core_x1.snap",
})
- c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
SnapID: s.AssertedSnapID("pc-kernel"),
File: "pc-kernel_2.snap",
})
- c.Check(seedYaml.Snaps[3], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[3], DeepEquals, &seed.Snap16{
Name: "core18",
SnapID: s.AssertedSnapID("core18"),
File: "core18_18.snap",
})
- c.Check(seedYaml.Snaps[4], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[4], DeepEquals, &seed.Snap16{
Name: "pc18",
SnapID: s.AssertedSnapID("pc18"),
File: "pc18_4.snap",
})
- c.Check(seedYaml.Snaps[5], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[5], DeepEquals, &seed.Snap16{
Name: "required-snap1",
SnapID: s.AssertedSnapID("required-snap1"),
File: "required-snap1_3.snap",
@@ -1692,32 +1692,32 @@ func (s *imageSuite) TestSetupSeedWithBaseAndLegacyCoreOrdering(c *C) {
c.Assert(err, IsNil)
c.Check(seedYaml.Snaps, HasLen, 6)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "snapd",
SnapID: s.AssertedSnapID("snapd"),
File: "snapd_18.snap",
})
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "core",
SnapID: s.AssertedSnapID("core"),
File: "core_3.snap",
})
- c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[2], DeepEquals, &seed.Snap16{
Name: "pc-kernel",
SnapID: s.AssertedSnapID("pc-kernel"),
File: "pc-kernel_2.snap",
})
- c.Check(seedYaml.Snaps[3], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[3], DeepEquals, &seed.Snap16{
Name: "core18",
SnapID: s.AssertedSnapID("core18"),
File: "core18_18.snap",
})
- c.Check(seedYaml.Snaps[4], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[4], DeepEquals, &seed.Snap16{
Name: "pc18",
SnapID: s.AssertedSnapID("pc18"),
File: "pc18_4.snap",
})
- c.Check(seedYaml.Snaps[5], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[5], DeepEquals, &seed.Snap16{
Name: "required-snap1",
SnapID: s.AssertedSnapID("required-snap1"),
File: "required-snap1_3.snap",
@@ -2020,7 +2020,7 @@ func (s *imageSuite) TestSetupSeedClassic(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
File: fn,
@@ -2082,7 +2082,7 @@ func (s *imageSuite) TestSetupSeedClassicWithLocalClassicSnap(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", "core_3.snap")
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
Name: "core",
SnapID: s.AssertedSnapID("core"),
File: "core_3.snap",
@@ -2090,7 +2090,7 @@ func (s *imageSuite) TestSetupSeedClassicWithLocalClassicSnap(c *C) {
p = filepath.Join(rootdir, "var/lib/snapd/seed/snaps", "classic-snap_x1.snap")
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
Name: "classic-snap",
File: "classic-snap_x1.snap",
Classic: true,
@@ -2152,7 +2152,7 @@ func (s *imageSuite) TestSetupSeedClassicSnapdOnly(c *C) {
p := filepath.Join(rootdir, "var/lib/snapd/seed/snaps", fn)
c.Check(osutil.FileExists(p), Equals, true)
- c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap{
+ c.Check(seedYaml.Snaps[i], DeepEquals, &seed.Snap16{
Name: info.InstanceName(),
SnapID: info.SnapID,
File: fn,
diff --git a/interfaces/apparmor/spec.go b/interfaces/apparmor/spec.go
index d813f9484d..ffbe5bd425 100644
--- a/interfaces/apparmor/spec.go
+++ b/interfaces/apparmor/spec.go
@@ -156,6 +156,7 @@ func (spec *Specification) AddLayout(si *snap.Info) {
bind := si.ExpandSnapVariables(l.Bind)
// Allow bind mounting the layout element.
fmt.Fprintf(&buf, " mount options=(rbind, rw) %s/ -> %s/,\n", bind, path)
+ fmt.Fprintf(&buf, " mount options=(rprivate) -> %s/,\n", path)
fmt.Fprintf(&buf, " umount %s/,\n", path)
// Allow constructing writable mimic in both bind-mount source and mount point.
WritableProfile(&buf, path, 2) // At least / and /some-top-level-directory
@@ -164,12 +165,14 @@ func (spec *Specification) AddLayout(si *snap.Info) {
bindFile := si.ExpandSnapVariables(l.BindFile)
// Allow bind mounting the layout element.
fmt.Fprintf(&buf, " mount options=(bind, rw) %s -> %s,\n", bindFile, path)
+ fmt.Fprintf(&buf, " mount options=(rprivate) -> %s,\n", path)
fmt.Fprintf(&buf, " umount %s,\n", path)
// Allow constructing writable mimic in both bind-mount source and mount point.
WritableFileProfile(&buf, path, 2) // At least / and /some-top-level-directory
WritableFileProfile(&buf, bindFile, 4) // At least /, /snap/, /snap/$SNAP_NAME and /snap/$SNAP_NAME/$SNAP_REVISION
case l.Type == "tmpfs":
fmt.Fprintf(&buf, " mount fstype=tmpfs tmpfs -> %s/,\n", path)
+ fmt.Fprintf(&buf, " mount options=(rprivate) -> %s/,\n", path)
fmt.Fprintf(&buf, " umount %s/,\n", path)
// Allow constructing writable mimic to mount point.
WritableProfile(&buf, path, 2) // At least / and /some-top-level-directory
@@ -264,10 +267,14 @@ func WritableMimicProfile(buf *bytes.Buffer, path string, assumedPrefixDepth int
fmt.Fprintf(buf, " mount options=(bind, rw) %s* -> %s*,\n", mimicAuxPath, mimicPath)
fmt.Fprintf(buf, " # Allow unmounting the auxiliary directory.\n"+
" # TODO: use fstype=tmpfs here for more strictness (LP: #1613403)\n")
+ fmt.Fprintf(buf, " mount options=(rprivate) -> %s,\n", mimicAuxPath)
fmt.Fprintf(buf, " umount %s,\n", mimicAuxPath)
fmt.Fprintf(buf, " # Allow unmounting the destination directory as well as anything\n"+
" # inside. This lets us perform the undo plan in case the writable\n"+
" # mimic fails.\n")
+ fmt.Fprintf(buf, " mount options=(rprivate) -> %s,\n", mimicPath)
+ fmt.Fprintf(buf, " mount options=(rprivate) -> %s*,\n", mimicPath)
+ fmt.Fprintf(buf, " mount options=(rprivate) -> %s*/,\n", mimicPath)
fmt.Fprintf(buf, " umount %s,\n", mimicPath)
fmt.Fprintf(buf, " umount %s*,\n", mimicPath)
fmt.Fprintf(buf, " umount %s*/,\n", mimicPath)
diff --git a/interfaces/apparmor/spec_test.go b/interfaces/apparmor/spec_test.go
index cbba954ae4..aa8727b93a 100644
--- a/interfaces/apparmor/spec_test.go
+++ b/interfaces/apparmor/spec_test.go
@@ -183,6 +183,7 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
profile0 := ` # Layout /etc/foo.conf: bind-file $SNAP/foo.conf
mount options=(bind, rw) /snap/vanguard/42/foo.conf -> /etc/foo.conf,
+ mount options=(rprivate) -> /etc/foo.conf,
umount /etc/foo.conf,
# Writable mimic /etc
# .. permissions for traversing the prefix that is assumed to exist
@@ -205,10 +206,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/etc/* -> /etc/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/etc/,
umount /tmp/.snap/etc/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /etc/,
+ mount options=(rprivate) -> /etc/*,
+ mount options=(rprivate) -> /etc/*/,
umount /etc/,
umount /etc/*,
umount /etc/*/,
@@ -235,10 +240,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/snap/vanguard/42/* -> /snap/vanguard/42/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/vanguard/42/,
umount /tmp/.snap/snap/vanguard/42/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/vanguard/42/,
+ mount options=(rprivate) -> /snap/vanguard/42/*,
+ mount options=(rprivate) -> /snap/vanguard/42/*/,
umount /snap/vanguard/42/,
umount /snap/vanguard/42/*,
umount /snap/vanguard/42/*/,
@@ -247,6 +256,7 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
profile1 := ` # Layout /usr/foo: bind $SNAP/usr/foo
mount options=(rbind, rw) /snap/vanguard/42/usr/foo/ -> /usr/foo/,
+ mount options=(rprivate) -> /usr/foo/,
umount /usr/foo/,
# Writable mimic /usr
# .. permissions for traversing the prefix that is assumed to exist
@@ -269,10 +279,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/usr/* -> /usr/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/usr/,
umount /tmp/.snap/usr/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /usr/,
+ mount options=(rprivate) -> /usr/*,
+ mount options=(rprivate) -> /usr/*/,
umount /usr/,
umount /usr/*,
umount /usr/*/,
@@ -299,10 +313,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/snap/vanguard/42/* -> /snap/vanguard/42/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/vanguard/42/,
umount /tmp/.snap/snap/vanguard/42/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/vanguard/42/,
+ mount options=(rprivate) -> /snap/vanguard/42/*,
+ mount options=(rprivate) -> /snap/vanguard/42/*/,
umount /snap/vanguard/42/,
umount /snap/vanguard/42/*,
umount /snap/vanguard/42/*/,
@@ -324,10 +342,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/snap/vanguard/42/usr/* -> /snap/vanguard/42/usr/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/vanguard/42/usr/,
umount /tmp/.snap/snap/vanguard/42/usr/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/vanguard/42/usr/,
+ mount options=(rprivate) -> /snap/vanguard/42/usr/*,
+ mount options=(rprivate) -> /snap/vanguard/42/usr/*/,
umount /snap/vanguard/42/usr/,
umount /snap/vanguard/42/usr/*,
umount /snap/vanguard/42/usr/*/,
@@ -357,10 +379,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/var/* -> /var/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/var/,
umount /tmp/.snap/var/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /var/,
+ mount options=(rprivate) -> /var/*,
+ mount options=(rprivate) -> /var/*/,
umount /var/,
umount /var/*,
umount /var/*/,
@@ -382,10 +408,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/var/cache/* -> /var/cache/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/var/cache/,
umount /tmp/.snap/var/cache/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /var/cache/,
+ mount options=(rprivate) -> /var/cache/*,
+ mount options=(rprivate) -> /var/cache/*/,
umount /var/cache/,
umount /var/cache/*,
umount /var/cache/*/,
@@ -394,6 +424,7 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
profile3 := ` # Layout /var/tmp: type tmpfs, mode: 01777
mount fstype=tmpfs tmpfs -> /var/tmp/,
+ mount options=(rprivate) -> /var/tmp/,
umount /var/tmp/,
# Writable mimic /var
# .. permissions for traversing the prefix that is assumed to exist
@@ -416,10 +447,14 @@ func (s *specSuite) TestApparmorSnippetsFromLayout(c *C) {
mount options=(bind, rw) /tmp/.snap/var/* -> /var/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/var/,
umount /tmp/.snap/var/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /var/,
+ mount options=(rprivate) -> /var/*,
+ mount options=(rprivate) -> /var/*/,
umount /var/,
umount /var/*,
umount /var/*/,
diff --git a/interfaces/apparmor/template.go b/interfaces/apparmor/template.go
index 1d7c261dc5..d0e23f2177 100644
--- a/interfaces/apparmor/template.go
+++ b/interfaces/apparmor/template.go
@@ -758,6 +758,7 @@ profile snap-update-ns.###SNAP_INSTANCE_NAME### (attach_disconnected) {
# Allow the content interface to bind fonts from the host filesystem
mount options=(ro bind) /var/lib/snapd/hostfs/usr/share/fonts/ -> /snap/###SNAP_INSTANCE_NAME###/*/**,
+ mount options=(rw private) -> /snap/###SNAP_INSTANCE_NAME###/*/**,
umount /snap/###SNAP_INSTANCE_NAME###/*/**,
# set up user mount namespace
diff --git a/interfaces/builtin/content.go b/interfaces/builtin/content.go
index ad7564d904..286fad5ee6 100644
--- a/interfaces/builtin/content.go
+++ b/interfaces/builtin/content.go
@@ -229,6 +229,7 @@ func (iface *contentInterface) AppArmorConnectedPlug(spec *apparmor.Specificatio
var buf bytes.Buffer
fmt.Fprintf(&buf, " # Read-write content sharing %s -> %s (w#%d)\n", plug.Ref(), slot.Ref(), i)
fmt.Fprintf(&buf, " mount options=(bind, rw) %s/ -> %s/,\n", source, target)
+ fmt.Fprintf(&buf, " mount options=(rprivate) -> %s/,\n", target)
fmt.Fprintf(&buf, " umount %s/,\n", target)
// TODO: The assumed prefix depth could be optimized to be more
// precise since content sharing can only take place in a fixed
@@ -256,6 +257,7 @@ func (iface *contentInterface) AppArmorConnectedPlug(spec *apparmor.Specificatio
fmt.Fprintf(&buf, " # Read-only content sharing %s -> %s (r#%d)\n", plug.Ref(), slot.Ref(), i)
fmt.Fprintf(&buf, " mount options=(bind) %s/ -> %s/,\n", source, target)
fmt.Fprintf(&buf, " remount options=(bind, ro) %s/,\n", target)
+ fmt.Fprintf(&buf, " mount options=(rprivate) -> %s/,\n", target)
fmt.Fprintf(&buf, " umount %s/,\n", target)
// Look at the TODO comment above.
apparmor.WritableProfile(&buf, source, 1)
diff --git a/interfaces/builtin/content_test.go b/interfaces/builtin/content_test.go
index 800a7eafdb..8cefef70c1 100644
--- a/interfaces/builtin/content_test.go
+++ b/interfaces/builtin/content_test.go
@@ -318,6 +318,7 @@ slots:
profile0 := ` # Read-only content sharing consumer:content -> producer:content (r#0)
mount options=(bind) /snap/producer/5/export/ -> /snap/consumer/7/import/,
remount options=(bind, ro) /snap/consumer/7/import/,
+ mount options=(rprivate) -> /snap/consumer/7/import/,
umount /snap/consumer/7/import/,
# Writable mimic /snap/producer/5
# .. permissions for traversing the prefix that is assumed to exist
@@ -339,10 +340,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/* -> /*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/,
umount /tmp/.snap/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /,
+ mount options=(rprivate) -> /*,
+ mount options=(rprivate) -> /*/,
umount /,
umount /*,
umount /*/,
@@ -364,10 +369,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/* -> /snap/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/,
umount /tmp/.snap/snap/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/,
+ mount options=(rprivate) -> /snap/*,
+ mount options=(rprivate) -> /snap/*/,
umount /snap/,
umount /snap/*,
umount /snap/*/,
@@ -389,10 +398,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/producer/* -> /snap/producer/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/producer/,
umount /tmp/.snap/snap/producer/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/producer/,
+ mount options=(rprivate) -> /snap/producer/*,
+ mount options=(rprivate) -> /snap/producer/*/,
umount /snap/producer/,
umount /snap/producer/*,
umount /snap/producer/*/,
@@ -414,10 +427,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/producer/5/* -> /snap/producer/5/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/producer/5/,
umount /tmp/.snap/snap/producer/5/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/producer/5/,
+ mount options=(rprivate) -> /snap/producer/5/*,
+ mount options=(rprivate) -> /snap/producer/5/*/,
umount /snap/producer/5/,
umount /snap/producer/5/*,
umount /snap/producer/5/*/,
@@ -441,10 +458,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/* -> /*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/,
umount /tmp/.snap/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /,
+ mount options=(rprivate) -> /*,
+ mount options=(rprivate) -> /*/,
umount /,
umount /*,
umount /*/,
@@ -466,10 +487,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/* -> /snap/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/,
umount /tmp/.snap/snap/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/,
+ mount options=(rprivate) -> /snap/*,
+ mount options=(rprivate) -> /snap/*/,
umount /snap/,
umount /snap/*,
umount /snap/*/,
@@ -491,10 +516,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/consumer/* -> /snap/consumer/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/consumer/,
umount /tmp/.snap/snap/consumer/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/consumer/,
+ mount options=(rprivate) -> /snap/consumer/*,
+ mount options=(rprivate) -> /snap/consumer/*/,
umount /snap/consumer/,
umount /snap/consumer/*,
umount /snap/consumer/*/,
@@ -516,10 +545,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/consumer/7/* -> /snap/consumer/7/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/consumer/7/,
umount /tmp/.snap/snap/consumer/7/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/consumer/7/,
+ mount options=(rprivate) -> /snap/consumer/7/*,
+ mount options=(rprivate) -> /snap/consumer/7/*/,
umount /snap/consumer/7/,
umount /snap/consumer/7/*,
umount /snap/consumer/7/*/,
@@ -577,6 +610,7 @@ slots:
updateNS := apparmorSpec.UpdateNS()
profile0 := ` # Read-write content sharing consumer:content -> producer:content (w#0)
mount options=(bind, rw) /var/snap/producer/5/export/ -> /var/snap/consumer/7/import/,
+ mount options=(rprivate) -> /var/snap/consumer/7/import/,
umount /var/snap/consumer/7/import/,
# Writable directory /var/snap/producer/5/export
/var/snap/producer/5/export/ rw,
@@ -640,6 +674,7 @@ slots:
updateNS := apparmorSpec.UpdateNS()
profile0 := ` # Read-write content sharing consumer:content -> producer:content (w#0)
mount options=(bind, rw) /var/snap/producer/common/export/ -> /var/snap/consumer/common/import/,
+ mount options=(rprivate) -> /var/snap/consumer/common/import/,
umount /var/snap/consumer/common/import/,
# Writable directory /var/snap/producer/common/export
/var/snap/producer/common/export/ rw,
@@ -738,6 +773,7 @@ slots:
updateNS := apparmorSpec.UpdateNS()
profile0 := ` # Read-write content sharing consumer:content -> producer:content (w#0)
mount options=(bind, rw) /var/snap/producer/common/write-common/ -> /var/snap/consumer/common/import/write-common/,
+ mount options=(rprivate) -> /var/snap/consumer/common/import/write-common/,
umount /var/snap/consumer/common/import/write-common/,
# Writable directory /var/snap/producer/common/write-common
/var/snap/producer/common/write-common/ rw,
@@ -753,6 +789,7 @@ slots:
profile1 := ` # Read-write content sharing consumer:content -> producer:content (w#1)
mount options=(bind, rw) /var/snap/producer/2/write-data/ -> /var/snap/consumer/common/import/write-data/,
+ mount options=(rprivate) -> /var/snap/consumer/common/import/write-data/,
umount /var/snap/consumer/common/import/write-data/,
# Writable directory /var/snap/producer/2/write-data
/var/snap/producer/2/write-data/ rw,
@@ -769,6 +806,7 @@ slots:
profile2 := ` # Read-only content sharing consumer:content -> producer:content (r#0)
mount options=(bind) /var/snap/producer/common/read-common/ -> /var/snap/consumer/common/import/read-common/,
remount options=(bind, ro) /var/snap/consumer/common/import/read-common/,
+ mount options=(rprivate) -> /var/snap/consumer/common/import/read-common/,
umount /var/snap/consumer/common/import/read-common/,
# Writable directory /var/snap/producer/common/read-common
/var/snap/producer/common/read-common/ rw,
@@ -785,6 +823,7 @@ slots:
profile3 := ` # Read-only content sharing consumer:content -> producer:content (r#1)
mount options=(bind) /var/snap/producer/2/read-data/ -> /var/snap/consumer/common/import/read-data/,
remount options=(bind, ro) /var/snap/consumer/common/import/read-data/,
+ mount options=(rprivate) -> /var/snap/consumer/common/import/read-data/,
umount /var/snap/consumer/common/import/read-data/,
# Writable directory /var/snap/producer/2/read-data
/var/snap/producer/2/read-data/ rw,
@@ -801,6 +840,7 @@ slots:
profile4 := ` # Read-only content sharing consumer:content -> producer:content (r#2)
mount options=(bind) /snap/producer/2/read-snap/ -> /var/snap/consumer/common/import/read-snap/,
remount options=(bind, ro) /var/snap/consumer/common/import/read-snap/,
+ mount options=(rprivate) -> /var/snap/consumer/common/import/read-snap/,
umount /var/snap/consumer/common/import/read-snap/,
# Writable mimic /snap/producer/2
# .. permissions for traversing the prefix that is assumed to exist
@@ -822,10 +862,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/* -> /*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/,
umount /tmp/.snap/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /,
+ mount options=(rprivate) -> /*,
+ mount options=(rprivate) -> /*/,
umount /,
umount /*,
umount /*/,
@@ -847,10 +891,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/* -> /snap/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/,
umount /tmp/.snap/snap/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/,
+ mount options=(rprivate) -> /snap/*,
+ mount options=(rprivate) -> /snap/*/,
umount /snap/,
umount /snap/*,
umount /snap/*/,
@@ -872,10 +920,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/producer/* -> /snap/producer/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/producer/,
umount /tmp/.snap/snap/producer/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/producer/,
+ mount options=(rprivate) -> /snap/producer/*,
+ mount options=(rprivate) -> /snap/producer/*/,
umount /snap/producer/,
umount /snap/producer/*,
umount /snap/producer/*/,
@@ -897,10 +949,14 @@ slots:
mount options=(bind, rw) /tmp/.snap/snap/producer/2/* -> /snap/producer/2/*,
# Allow unmounting the auxiliary directory.
# TODO: use fstype=tmpfs here for more strictness (LP: #1613403)
+ mount options=(rprivate) -> /tmp/.snap/snap/producer/2/,
umount /tmp/.snap/snap/producer/2/,
# Allow unmounting the destination directory as well as anything
# inside. This lets us perform the undo plan in case the writable
# mimic fails.
+ mount options=(rprivate) -> /snap/producer/2/,
+ mount options=(rprivate) -> /snap/producer/2/*,
+ mount options=(rprivate) -> /snap/producer/2/*/,
umount /snap/producer/2/,
umount /snap/producer/2/*,
umount /snap/producer/2/*/,
diff --git a/interfaces/builtin/wayland.go b/interfaces/builtin/wayland.go
index 219f65434e..151471a2fc 100644
--- a/interfaces/builtin/wayland.go
+++ b/interfaces/builtin/wayland.go
@@ -59,6 +59,7 @@ owner /run/user/[0-9]*/wayland-[0-9]* rwk,
# Allow reading an Xwayland Xauth file
# (see https://gitlab.gnome.org/GNOME/mutter/merge_requests/626)
/run/user/[0-9]*/.mutter-Xwaylandauth.* r,
+/run/user/[0-9]*/mutter/Xauthority r,
# Allow write access to create /run/user/* to create XDG_RUNTIME_DIR (until
# lp:1738197 is fixed). Note this is not needed if creating a session using
diff --git a/interfaces/builtin/x11.go b/interfaces/builtin/x11.go
index d038dd5fb5..9d03374e64 100644
--- a/interfaces/builtin/x11.go
+++ b/interfaces/builtin/x11.go
@@ -129,6 +129,8 @@ owner /run/user/[0-9]*/.Xauthority r,
# Allow reading an Xwayland Xauth file
# (see https://gitlab.gnome.org/GNOME/mutter/merge_requests/626)
owner /run/user/[0-9]*/.mutter-Xwaylandauth.* r,
+owner /run/user/[0-9]*/mutter/Xauthority r,
+
# Needed by QtSystems on X to detect mouse and keyboard. Note, the 'netlink
# raw' rule is not finely mediated by apparmor so we mediate with seccomp arg
diff --git a/overlord/devicestate/firstboot.go b/overlord/devicestate/firstboot.go
index 170a0edd16..08da244228 100644
--- a/overlord/devicestate/firstboot.go
+++ b/overlord/devicestate/firstboot.go
@@ -1,7 +1,7 @@
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
- * Copyright (C) 2014-2015 Canonical Ltd
+ * Copyright (C) 2014-2019 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
@@ -22,14 +22,11 @@ package devicestate
import (
"errors"
"fmt"
- "path/filepath"
"sort"
"github.com/snapcore/snapd/asserts"
- "github.com/snapcore/snapd/asserts/snapasserts"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/i18n"
- "github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/overlord/assertstate"
"github.com/snapcore/snapd/overlord/devicestate/internal"
"github.com/snapcore/snapd/overlord/snapstate"
@@ -37,13 +34,15 @@ import (
"github.com/snapcore/snapd/release"
"github.com/snapcore/snapd/seed"
"github.com/snapcore/snapd/snap"
- "github.com/snapcore/snapd/snap/naming"
"github.com/snapcore/snapd/timings"
)
var errNothingToDo = errors.New("nothing to do")
-func installSeedSnap(st *state.State, sn *seed.Snap, flags snapstate.Flags, tm timings.Measurer) (*state.TaskSet, *snap.Info, error) {
+func installSeedSnap(st *state.State, sn *seed.Snap, flags snapstate.Flags) (*state.TaskSet, *snap.Info, error) {
+ if sn.Required {
+ flags.Required = true
+ }
if sn.Classic {
flags.Classic = true
}
@@ -51,29 +50,7 @@ func installSeedSnap(st *state.State, sn *seed.Snap, flags snapstate.Flags, tm t
flags.DevMode = true
}
- path := filepath.Join(dirs.SnapSeedDir, "snaps", sn.File)
-
- var sideInfo snap.SideInfo
- if sn.Unasserted {
- sideInfo.RealName = sn.Name
- } else {
- var si *snap.SideInfo
- var err error
- timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", sn.Name), func(nested timings.Measurer) {
- si, err = snapasserts.DeriveSideInfo(path, assertstate.DB(st))
- })
- if asserts.IsNotFound(err) {
- return nil, nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path)
- }
- if err != nil {
- return nil, nil, err
- }
- sideInfo = *si
- sideInfo.Private = sn.Private
- sideInfo.Contact = sn.Contact
- }
-
- return snapstate.InstallPath(st, &sideInfo, path, "", sn.Channel, flags)
+ return snapstate.InstallPath(st, sn.SideInfo, sn.Path, "", sn.Channel, flags)
}
func trivialSeeding(st *state.State, markSeeded *state.Task) []*state.TaskSet {
@@ -97,10 +74,15 @@ func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.T
markSeeded := st.NewTask("mark-seeded", i18n.G("Mark system seeded"))
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ if err != nil {
+ return nil, err
+ }
+
// ack all initial assertions
var model *asserts.Model
timings.Run(tm, "import-assertions", "import assertions from seed", func(nested timings.Measurer) {
- model, err = importAssertionsFromSeed(st)
+ model, err = importAssertionsFromSeed(st, deviceSeed)
})
if err == errNothingToDo {
return trivialSeeding(st, markSeeded), nil
@@ -109,27 +91,23 @@ func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.T
return nil, err
}
- seedYamlFile := filepath.Join(dirs.SnapSeedDir, "seed.yaml")
- if release.OnClassic && !osutil.FileExists(seedYamlFile) {
+ err = deviceSeed.LoadMeta(tm)
+ if release.OnClassic && err == seed.ErrNoMeta {
// on classic it is ok to not seed any snaps
return trivialSeeding(st, markSeeded), nil
}
-
- seedMeta, err := seed.ReadYaml(seedYamlFile)
if err != nil {
return nil, err
}
- seedSnaps := seedMeta.Snaps
- required := getAllRequiredSnapsForModel(model)
- seeding := make(map[string]*seed.Snap, len(seedSnaps))
- for _, sn := range seedSnaps {
- seeding[sn.Name] = sn
+ essentialSeedSnaps := deviceSeed.EssentialSnaps()
+ seedSnaps, err := deviceSeed.ModeSnaps("run") // XXX mode should be passed in
+ if err != nil {
+ return nil, err
}
- alreadySeeded := make(map[string]bool, 3)
// allSnapInfos are collected for cross-check validation of bases
- allSnapInfos := make(map[string]*snap.Info, len(seedSnaps))
+ allSnapInfos := make(map[string]*snap.Info, len(essentialSeedSnaps)+len(seedSnaps))
tsAll := []*state.TaskSet{}
configTss := []*state.TaskSet{}
@@ -140,95 +118,40 @@ func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.T
}
return append(all, ts)
}
-
- baseSnap := "core"
- classicWithSnapd := false
- if model.Base() != "" {
- baseSnap = model.Base()
- }
- if _, ok := seeding["snapd"]; release.OnClassic && ok {
- classicWithSnapd = true
- // there is no system-wide base as such
- // if there is a gadget we will install its base first though
- baseSnap = ""
- }
-
- var installSeedEssential func(snapName string) error
- var installGadgetBase func(gadget *snap.Info) error
- installSeedEssential = func(snapName string) error {
- // be idempotent for installGadgetBase use
- if alreadySeeded[snapName] {
- return nil
- }
- seedSnap := seeding[snapName]
- if seedSnap == nil {
- return fmt.Errorf("cannot proceed without seeding %q", snapName)
- }
- ts, info, err := installSeedSnap(st, seedSnap, snapstate.Flags{SkipConfigure: true, Required: true}, tm)
- if err != nil {
- return err
- }
- if info.GetType() == snap.TypeGadget {
- // always make sure the base of gadget is installed first
- if err := installGadgetBase(info); err != nil {
- return err
- }
- }
- tsAll = chainTs(tsAll, ts)
- alreadySeeded[snapName] = true
- allSnapInfos[snapName] = info
- return nil
- }
- installGadgetBase = func(gadget *snap.Info) error {
- gadgetBase := gadget.Base
- if gadgetBase == "" {
- gadgetBase = "core"
- }
- // Sanity check
- // TODO: do we want to relax this? the new logic would allow
- // but it might just be confusing for now
- if baseSnap != "" && gadgetBase != baseSnap {
- return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base())
+ chainSorted := func(infos []*snap.Info, infoToTs map[*snap.Info]*state.TaskSet) {
+ sort.Stable(snap.ByType(infos))
+ for _, info := range infos {
+ ts := infoToTs[info]
+ tsAll = chainTs(tsAll, ts)
}
- return installSeedEssential(gadgetBase)
}
- // if there are snaps to seed, core/base needs to be seeded too
- if len(seedSnaps) != 0 {
- // ensure "snapd" snap is installed first
- if model.Base() != "" || classicWithSnapd {
- if err := installSeedEssential("snapd"); err != nil {
- return nil, err
- }
- }
- if !classicWithSnapd {
- if err := installSeedEssential(baseSnap); err != nil {
- return nil, err
- }
- }
+ essInfoToTs := make(map[*snap.Info]*state.TaskSet, len(essentialSeedSnaps))
+ essInfos := make([]*snap.Info, 0, len(essentialSeedSnaps))
+
+ if len(essentialSeedSnaps) != 0 {
// we *always* configure "core" here even if bases are used
// for booting. "core" if where the system config lives.
configTss = chainTs(configTss, snapstate.ConfigureSnap(st, "core", snapstate.UseConfigDefaults))
}
- if kernelName := model.Kernel(); kernelName != "" {
- if err := installSeedEssential(kernelName); err != nil {
- return nil, err
- }
- configTs := snapstate.ConfigureSnap(st, kernelName, snapstate.UseConfigDefaults)
- // wait for the previous configTss
- configTss = chainTs(configTss, configTs)
- }
-
- if gadgetName := model.Gadget(); gadgetName != "" {
- err := installSeedEssential(gadgetName)
+ for _, seedSnap := range essentialSeedSnaps {
+ ts, info, err := installSeedSnap(st, seedSnap, snapstate.Flags{SkipConfigure: true})
if err != nil {
return nil, err
}
- configTs := snapstate.ConfigureSnap(st, gadgetName, snapstate.UseConfigDefaults)
- // wait for the previous configTss
- configTss = chainTs(configTss, configTs)
+ if info.GetType() == snap.TypeKernel || info.GetType() == snap.TypeGadget {
+ configTs := snapstate.ConfigureSnap(st, info.SnapName(), snapstate.UseConfigDefaults)
+ // wait for the previous configTss
+ configTss = chainTs(configTss, configTs)
+ }
+ essInfos = append(essInfos, info)
+ essInfoToTs[info] = ts
+ allSnapInfos[info.SnapName()] = info
}
+ // now add/chain the tasksets in the right order based on essential
+ // snap types
+ chainSorted(essInfos, essInfoToTs)
// chain together configuring core, kernel, and gadget after
// installing them so that defaults are availabble from gadget
@@ -241,24 +164,15 @@ func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.T
infoToTs := make(map[*snap.Info]*state.TaskSet, len(seedSnaps))
infos := make([]*snap.Info, 0, len(seedSnaps))
- for _, sn := range seedSnaps {
- if alreadySeeded[sn.Name] {
- continue
- }
-
+ for _, seedSnap := range seedSnaps {
var flags snapstate.Flags
- // TODO|XXX: turn SeedSnap into a SnapRef
- if required.Contains(naming.Snap(sn.Name)) {
- flags.Required = true
- }
-
- ts, info, err := installSeedSnap(st, sn, flags, tm)
+ ts, info, err := installSeedSnap(st, seedSnap, flags)
if err != nil {
return nil, err
}
infos = append(infos, info)
infoToTs[info] = ts
- allSnapInfos[info.InstanceName()] = info
+ allSnapInfos[info.SnapName()] = info
}
// validate that all snaps have bases
@@ -270,11 +184,7 @@ func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.T
// now add/chain the tasksets in the right order, note that we
// only have tasksets that we did not already seeded
- sort.Stable(snap.ByType(infos))
- for _, info := range infos {
- ts := infoToTs[info]
- tsAll = chainTs(tsAll, ts)
- }
+ chainSorted(infos, infoToTs)
if len(tsAll) == 0 {
return nil, fmt.Errorf("cannot proceed, no snaps to seed")
@@ -297,28 +207,20 @@ func populateStateFromSeedImpl(st *state.State, tm timings.Measurer) ([]*state.T
return tsAll, nil
}
-func importAssertionsFromSeed(st *state.State) (*asserts.Model, error) {
+func importAssertionsFromSeed(st *state.State, deviceSeed seed.Seed) (*asserts.Model, error) {
// TODO: use some kind of context fo Device/SetDevice?
device, err := internal.Device(st)
if err != nil {
return nil, err
}
- assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions")
// collect and
// set device,model from the model assertion
- var modelRef *asserts.Ref
- checkForModel := func(ref *asserts.Ref) error {
- if ref.Type == asserts.ModelType {
- if modelRef != nil && modelRef.Unique() != ref.Unique() {
- return fmt.Errorf("cannot add more than one model assertion")
- }
- modelRef = ref
- }
- return nil
+ commitTo := func(batch *asserts.Batch) error {
+ return assertstate.AddBatch(st, batch, nil)
}
- batch, err := seed.LoadAssertions(assertSeedDir, checkForModel)
+ err = deviceSeed.LoadAssertions(assertstate.DB(st), commitTo)
if err == seed.ErrNoAssertions && release.OnClassic {
// on classic seeding is optional
// set the fallback model
@@ -332,20 +234,10 @@ func importAssertionsFromSeed(st *state.State) (*asserts.Model, error) {
return nil, err
}
- // verify we have one model assertion
- if modelRef == nil {
- return nil, fmt.Errorf("need a model assertion")
- }
-
- if err := assertstate.AddBatch(st, batch, nil); err != nil {
- return nil, err
- }
-
- a, err := modelRef.Resolve(assertstate.DB(st).Find)
+ modelAssertion, err := deviceSeed.Model()
if err != nil {
- return nil, fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err)
+ return nil, err
}
- modelAssertion := a.(*asserts.Model)
classicModel := modelAssertion.Classic()
if release.OnClassic != classicModel {
diff --git a/overlord/devicestate/firstboot_test.go b/overlord/devicestate/firstboot_test.go
index 8e877c1d6d..f94027ba75 100644
--- a/overlord/devicestate/firstboot_test.go
+++ b/overlord/devicestate/firstboot_test.go
@@ -49,6 +49,7 @@ import (
"github.com/snapcore/snapd/overlord/snapstate"
"github.com/snapcore/snapd/overlord/state"
"github.com/snapcore/snapd/release"
+ "github.com/snapcore/snapd/seed"
"github.com/snapcore/snapd/seed/seedtest"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/snap/snaptest"
@@ -987,7 +988,10 @@ func (s *FirstBootTestSuite) TestImportAssertionsFromSeedClassicModelMismatch(c
st.Lock()
defer st.Unlock()
- _, err = devicestate.ImportAssertionsFromSeed(st)
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ c.Assert(err, IsNil)
+
+ _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model")
}
@@ -1004,7 +1008,10 @@ func (s *FirstBootTestSuite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c
st.Lock()
defer st.Unlock()
- _, err = devicestate.ImportAssertionsFromSeed(st)
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ c.Assert(err, IsNil)
+
+ _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model")
}
@@ -1027,7 +1034,10 @@ func (s *FirstBootTestSuite) TestImportAssertionsFromSeedHappy(c *C) {
st.Lock()
defer st.Unlock()
- model, err := devicestate.ImportAssertionsFromSeed(st)
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ c.Assert(err, IsNil)
+
+ model, err := devicestate.ImportAssertionsFromSeed(st, deviceSeed)
c.Assert(err, IsNil)
c.Assert(model, NotNil)
@@ -1065,9 +1075,12 @@ func (s *FirstBootTestSuite) TestImportAssertionsFromSeedMissingSig(c *C) {
}
}
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ c.Assert(err, IsNil)
+
// try import and verify that its rejects because other assertions are
// missing
- _, err := devicestate.ImportAssertionsFromSeed(st)
+ _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*")
}
@@ -1083,10 +1096,13 @@ func (s *FirstBootTestSuite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) {
model2 := s.Brands.Model("my-brand", "my-second-model", s.modelHeaders("my-second-model"))
s.WriteAssertions("model2", model2)
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ c.Assert(err, IsNil)
+
// try import and verify that its rejects because other assertions are
// missing
- _, err := devicestate.ImportAssertionsFromSeed(st)
- c.Assert(err, ErrorMatches, "cannot add more than one model assertion")
+ _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
+ c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed")
}
func (s *FirstBootTestSuite) TestImportAssertionsFromSeedNoModelAsserts(c *C) {
@@ -1101,10 +1117,13 @@ func (s *FirstBootTestSuite) TestImportAssertionsFromSeedNoModelAsserts(c *C) {
}
}
+ deviceSeed, err := seed.Open(dirs.SnapSeedDir)
+ c.Assert(err, IsNil)
+
// try import and verify that its rejects because other assertions are
// missing
- _, err := devicestate.ImportAssertionsFromSeed(st)
- c.Assert(err, ErrorMatches, "need a model assertion")
+ _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
+ c.Assert(err, ErrorMatches, "seed must have a model assertion")
}
type core18SnapsOpts struct {
@@ -1209,7 +1228,7 @@ snaps:
tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
c.Assert(err, IsNil)
- checkOrder(c, tsAll, "snapd", "core18", "pc-kernel", "pc")
+ checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc")
// now run the change and check the result
// use the expected kind otherwise settle with start another one
@@ -1331,7 +1350,7 @@ snaps:
tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
c.Assert(err, IsNil)
- checkOrder(c, tsAll, "snapd", "core18", "pc-kernel", "pc", "other-base", "snap-req-other-base")
+ checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base")
}
func (s *FirstBootTestSuite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) {
diff --git a/overlord/snapstate/handlers_link_test.go b/overlord/snapstate/handlers_link_test.go
index e9a4143b1e..2037a15f1b 100644
--- a/overlord/snapstate/handlers_link_test.go
+++ b/overlord/snapstate/handlers_link_test.go
@@ -387,7 +387,7 @@ func (s *linkSnapSuite) TestDoLinkSnapTryToCleanupOnError(c *C) {
c.Assert(err, Equals, state.ErrNoState)
// tried to cleanup
- c.Check(s.fakeBackend.ops, DeepEquals, fakeOps{
+ expected := fakeOps{
{
op: "candidate",
sinfo: *si,
@@ -400,7 +400,11 @@ func (s *linkSnapSuite) TestDoLinkSnapTryToCleanupOnError(c *C) {
op: "unlink-snap",
path: filepath.Join(dirs.SnapMountDir, "foo/35"),
},
- })
+ }
+
+ // start with an easier-to-read error if this fails:
+ c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
+ c.Check(s.fakeBackend.ops, DeepEquals, expected)
}
func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreRestarts(c *C) {
diff --git a/overlord/snapstate/snapstate_test.go b/overlord/snapstate/snapstate_test.go
index d9243193e6..3b4d46c487 100644
--- a/overlord/snapstate/snapstate_test.go
+++ b/overlord/snapstate/snapstate_test.go
@@ -6725,6 +6725,7 @@ version: 1.0`)
},
}
+ // start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
c.Check(s.fakeBackend.ops, DeepEquals, expected)
@@ -6788,41 +6789,64 @@ epoch: 1*
s.settle(c)
s.state.Lock()
- ops := s.fakeBackend.ops
- // ensure only local install was run, i.e. first action is pseudo-action current
- c.Assert(ops.Ops(), HasLen, 11)
- c.Check(ops[0].op, Equals, "current")
- c.Check(ops[0].old, Equals, filepath.Join(dirs.SnapMountDir, "mock/x2"))
- // and setup-snap
- c.Check(ops[1].op, Equals, "setup-snap")
- c.Check(ops[1].name, Matches, "mock")
- c.Check(ops[1].path, Matches, `.*/mock_1.0_all.snap`)
- c.Check(ops[1].revno, Equals, snap.R("x3"))
- // and cleanup
- c.Check(ops[len(ops)-1], DeepEquals, fakeOp{
- op: "cleanup-trash",
- name: "mock",
- revno: snap.R("x3"),
- })
-
- c.Check(ops[3].op, Equals, "unlink-snap")
- c.Check(ops[3].path, Equals, filepath.Join(dirs.SnapMountDir, "mock/x2"))
-
- c.Check(ops[4].op, Equals, "copy-data")
- c.Check(ops[4].path, Equals, filepath.Join(dirs.SnapMountDir, "mock/x3"))
- c.Check(ops[4].old, Equals, filepath.Join(dirs.SnapMountDir, "mock/x2"))
-
- c.Check(ops[5].op, Equals, "setup-profiles:Doing")
- c.Check(ops[5].name, Equals, "mock")
- c.Check(ops[5].revno, Equals, snap.R(-3))
+ expected := fakeOps{
+ {
+ op: "current",
+ old: filepath.Join(dirs.SnapMountDir, "mock/x2"),
+ },
+ {
+ op: "setup-snap",
+ name: "mock",
+ path: mockSnap,
+ revno: snap.R("x3"),
+ },
+ {
+ op: "remove-snap-aliases",
+ name: "mock",
+ },
+ {
+ op: "unlink-snap",
+ path: filepath.Join(dirs.SnapMountDir, "mock/x2"),
+ },
+ {
+ op: "copy-data",
+ path: filepath.Join(dirs.SnapMountDir, "mock/x3"),
+ old: filepath.Join(dirs.SnapMountDir, "mock/x2"),
+ },
+ {
+ op: "setup-profiles:Doing",
+ name: "mock",
+ revno: snap.R(-3),
+ },
+ {
+ op: "candidate",
+ sinfo: snap.SideInfo{
+ RealName: "mock",
+ Revision: snap.R(-3),
+ },
+ },
+ {
+ op: "link-snap",
+ path: filepath.Join(dirs.SnapMountDir, "mock/x3"),
+ },
+ {
+ op: "auto-connect:Doing",
+ name: "mock",
+ revno: snap.R("x3"),
+ },
+ {
+ op: "update-aliases",
+ },
+ {
+ op: "cleanup-trash",
+ name: "mock",
+ revno: snap.R("x3"),
+ },
+ }
- c.Check(ops[6].op, Equals, "candidate")
- c.Check(ops[6].sinfo, DeepEquals, snap.SideInfo{
- RealName: "mock",
- Revision: snap.R(-3),
- })
- c.Check(ops[7].op, Equals, "link-snap")
- c.Check(ops[7].path, Equals, filepath.Join(dirs.SnapMountDir, "mock/x3"))
+ // start with an easier-to-read error if this fails:
+ c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
+ c.Check(s.fakeBackend.ops, DeepEquals, expected)
// verify snapSetup info
var snapsup snapstate.SnapSetup
@@ -6942,6 +6966,7 @@ epoch: 1*
revno: snap.R("x1"),
},
}
+ // start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
c.Check(s.fakeBackend.ops, DeepEquals, expected)
diff --git a/seed/export_test.go b/seed/export_test.go
new file mode 100644
index 0000000000..62c5d75fa6
--- /dev/null
+++ b/seed/export_test.go
@@ -0,0 +1,24 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package seed
+
+var (
+ LoadAssertions = loadAssertions
+)
diff --git a/seed/helpers.go b/seed/helpers.go
index e76ea67d58..b0f2c8e98e 100644
--- a/seed/helpers.go
+++ b/seed/helpers.go
@@ -20,20 +20,42 @@
package seed
import (
- "errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/snapcore/snapd/asserts"
+ "github.com/snapcore/snapd/asserts/sysdb"
)
-var ErrNoAssertions = errors.New("no seed assertions")
+var trusted = sysdb.Trusted()
-func LoadAssertions(assertsDir string, loaded func(*asserts.Ref) error) (*asserts.Batch, error) {
- batch := asserts.NewBatch(nil)
+func MockTrusted(mockTrusted []asserts.Assertion) (restore func()) {
+ prevTrusted := trusted
+ trusted = mockTrusted
+ return func() {
+ trusted = prevTrusted
+ }
+}
+
+func newMemAssertionsDB() (db asserts.RODatabase, commitTo func(*asserts.Batch) error, err error) {
+ memDB, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
+ Backstore: asserts.NewMemoryBackstore(),
+ Trusted: trusted,
+ })
+ if err != nil {
+ return nil, nil, err
+ }
+ commitTo = func(b *asserts.Batch) error {
+ return b.CommitTo(memDB, nil)
+ }
+
+ return memDB, commitTo, nil
+}
+
+func loadAssertions(assertsDir string, loadedFunc func(*asserts.Ref) error) (*asserts.Batch, error) {
dc, err := ioutil.ReadDir(assertsDir)
if err != nil {
if os.IsNotExist(err) {
@@ -41,15 +63,17 @@ func LoadAssertions(assertsDir string, loaded func(*asserts.Ref) error) (*assert
}
return nil, fmt.Errorf("cannot read assertions dir: %s", err)
}
+
+ batch := asserts.NewBatch(nil)
for _, fi := range dc {
fn := filepath.Join(assertsDir, fi.Name())
refs, err := readAsserts(batch, fn)
if err != nil {
return nil, fmt.Errorf("cannot read assertions: %s", err)
}
- if loaded != nil {
+ if loadedFunc != nil {
for _, ref := range refs {
- if err := loaded(ref); err != nil {
+ if err := loadedFunc(ref); err != nil {
return nil, err
}
}
diff --git a/seed/seed.go b/seed/seed.go
new file mode 100644
index 0000000000..e65b2c4ed8
--- /dev/null
+++ b/seed/seed.go
@@ -0,0 +1,98 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// Package seed implements loading and validating of seed data.
+package seed
+
+import (
+ "errors"
+
+ "github.com/snapcore/snapd/asserts"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/timings"
+)
+
+var (
+ ErrNoAssertions = errors.New("no seed assertions")
+ ErrNoMeta = errors.New("no seed metadata")
+)
+
+// Snap holds the details of a snap in a seed.
+type Snap struct {
+ Path string
+
+ SideInfo *snap.SideInfo
+
+ Essential bool
+ Required bool
+
+ // options
+ Channel string
+ DevMode bool
+ Classic bool
+}
+
+func (s *Snap) SnapName() string {
+ return s.SideInfo.RealName
+}
+
+func (s *Snap) ID() string {
+ return s.SideInfo.SnapID
+}
+
+// Seed supports loading assertions and seed snaps' metadata.
+type Seed interface {
+ // LoadAssertions loads all assertions from the seed with
+ // cross-checks. A read-only view on an assertions database
+ // can be passed in together with a commitTo function which
+ // will be used to commit the assertions to the underlying
+ // database. If db is nil an internal temporary database will
+ // be setup instead. ErrNoAssertions will be returned if there
+ // is no assertions directory in the seed, this is legitimate
+ // only on classic.
+ LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error
+
+ // Model returns the seed provided model assertion. It is an
+ // error to call Model before LoadAssertions.
+ Model() (*asserts.Model, error)
+
+ // LoadMeta loads the seed and seed's snaps metadata. It can
+ // return ErrNoMeta if there is no metadata nor snaps in the
+ // seed, this is legitimate only on classic. It is an error to
+ // call LoadMeta before LoadAssertions.
+ LoadMeta(tm timings.Measurer) error
+
+ // UsesSnapdSnap returns whether the system as defined by the
+ // seed will use the snapd snap, after LoadMeta.
+ UsesSnapdSnap() bool
+
+ // EssentialSnaps returns the essential snaps as defined by
+ // the seed, after LoadMeta.
+ EssentialSnaps() []*Snap
+
+ // ModeSnaps returns the snaps that should be available
+ // in the given mode as defined by the seed, after LoadMeta.
+ ModeSnaps(mode string) ([]*Snap, error)
+}
+
+// Open returns a Seed implementation for the seed at seedDir.
+// TODO: more parameters for the Core20 case
+func Open(seedDir string) (Seed, error) {
+ return &seed16{seedDir: seedDir}, nil
+}
diff --git a/seed/seed16.go b/seed/seed16.go
new file mode 100644
index 0000000000..a57418043d
--- /dev/null
+++ b/seed/seed16.go
@@ -0,0 +1,294 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2014-2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package seed
+
+/* ATTN this should *not* use:
+
+* dirs package: it is passed an explicit directory to work on
+
+* release.OnClassic: it assumes classic based on the model classic
+ option; consistency between system and model can/must be enforced
+ elsewhere
+
+*/
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "github.com/snapcore/snapd/asserts"
+ "github.com/snapcore/snapd/asserts/snapasserts"
+ "github.com/snapcore/snapd/osutil"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/snap/naming"
+ "github.com/snapcore/snapd/timings"
+)
+
+type seed16 struct {
+ seedDir string
+
+ db asserts.RODatabase
+
+ model *asserts.Model
+
+ snaps []*Snap
+ essentialSnapsNum int
+
+ usesSnapdSnap bool
+}
+
+func (s *seed16) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error {
+ if db == nil {
+ // a db was not provided, create an internal temporary one
+ var err error
+ db, commitTo, err = newMemAssertionsDB()
+ if err != nil {
+ return err
+ }
+ }
+
+ assertSeedDir := filepath.Join(s.seedDir, "assertions")
+ // collect assertions and find model assertion
+ var modelRef *asserts.Ref
+ checkForModel := func(ref *asserts.Ref) error {
+ if ref.Type == asserts.ModelType {
+ if modelRef != nil && modelRef.Unique() != ref.Unique() {
+ return fmt.Errorf("cannot have multiple model assertions in seed")
+ }
+ modelRef = ref
+ }
+ return nil
+ }
+
+ batch, err := loadAssertions(assertSeedDir, checkForModel)
+ if err != nil {
+ return err
+ }
+
+ // verify we have one model assertion
+ if modelRef == nil {
+ return fmt.Errorf("seed must have a model assertion")
+ }
+
+ if err := commitTo(batch); err != nil {
+ return err
+ }
+
+ a, err := modelRef.Resolve(db.Find)
+ if err != nil {
+ return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err)
+ }
+
+ // remember db for later use
+ s.db = db
+ s.model = a.(*asserts.Model)
+
+ return nil
+}
+
+func (s *seed16) Model() (*asserts.Model, error) {
+ if s.model == nil {
+ return nil, fmt.Errorf("internal error: model assertion unset")
+ }
+ return s.model, nil
+}
+
+func (s *seed16) addSnap(sn *Snap16, tm timings.Measurer) (*Snap, error) {
+ path := filepath.Join(s.seedDir, "snaps", sn.File)
+ seedSnap := &Snap{
+ Path: path,
+ // TODO|XXX: make sure channel is right for pinned tracks
+ Channel: sn.Channel,
+ Classic: sn.Classic,
+ DevMode: sn.DevMode,
+ }
+
+ var sideInfo snap.SideInfo
+ if sn.Unasserted {
+ sideInfo.RealName = sn.Name
+ } else {
+ var si *snap.SideInfo
+ var err error
+ timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", sn.Name), func(nested timings.Measurer) {
+ si, err = snapasserts.DeriveSideInfo(path, s.db)
+ })
+ if asserts.IsNotFound(err) {
+ return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path)
+ }
+ if err != nil {
+ return nil, err
+ }
+ sideInfo = *si
+ sideInfo.Private = sn.Private
+ sideInfo.Contact = sn.Contact
+ }
+
+ seedSnap.SideInfo = &sideInfo
+
+ s.snaps = append(s.snaps, seedSnap)
+
+ return seedSnap, nil
+}
+
+func (s *seed16) LoadMeta(tm timings.Measurer) error {
+ model, err := s.Model()
+ if err != nil {
+ return err
+ }
+
+ seedYamlFile := filepath.Join(s.seedDir, "seed.yaml")
+ if !osutil.FileExists(seedYamlFile) {
+ return ErrNoMeta
+ }
+
+ seedYaml, err := ReadYaml(seedYamlFile)
+ if err != nil {
+ return err
+ }
+ yamlSnaps := seedYaml.Snaps
+
+ required := naming.NewSnapSet(model.RequiredWithEssentialSnaps())
+ seeding := make(map[string]*Snap16, len(yamlSnaps))
+ for _, sn := range yamlSnaps {
+ seeding[sn.Name] = sn
+ }
+ added := make(map[string]bool, 3)
+ classic := model.Classic()
+ _, s.usesSnapdSnap = seeding["snapd"]
+
+ baseSnap := "core"
+ classicWithSnapd := false
+ if model.Base() != "" {
+ baseSnap = model.Base()
+ }
+ if classic && s.usesSnapdSnap {
+ classicWithSnapd = true
+ // there is no system-wide base as such
+ // if there is a gadget we will install its base first though
+ baseSnap = ""
+ }
+
+ // add the essential snaps
+ addEssential := func(snapName string) (*Snap, error) {
+ // be idempotent
+ if added[snapName] {
+ return nil, nil
+ }
+ yamlSnap := seeding[snapName]
+ if yamlSnap == nil {
+ return nil, fmt.Errorf("essential snap %q required by the model is missing in the seed", snapName)
+ }
+
+ seedSnap, err := s.addSnap(yamlSnap, tm)
+ if err != nil {
+ return nil, err
+ }
+
+ seedSnap.Essential = true
+ seedSnap.Required = true
+ added[snapName] = true
+
+ return seedSnap, nil
+ }
+
+ // if there are snaps to seed, core/base needs to be seeded too
+ if len(yamlSnaps) != 0 {
+ // ensure "snapd" snap is installed first
+ if model.Base() != "" || classicWithSnapd {
+ if _, err := addEssential("snapd"); err != nil {
+ return err
+ }
+ }
+ if !classicWithSnapd {
+ if _, err := addEssential(baseSnap); err != nil {
+ return err
+ }
+ }
+ }
+
+ if kernelName := model.Kernel(); kernelName != "" {
+ if _, err := addEssential(kernelName); err != nil {
+ return err
+ }
+ }
+
+ if gadgetName := model.Gadget(); gadgetName != "" {
+ gadget, err := addEssential(gadgetName)
+ if err != nil {
+ return err
+ }
+
+ // always make sure the base of gadget is installed first
+ snapf, err := snap.Open(gadget.Path)
+ if err != nil {
+ return err
+ }
+ info, err := snap.ReadInfoFromSnapFile(snapf, gadget.SideInfo)
+ if err != nil {
+ return err
+ }
+ gadgetBase := info.Base
+ if gadgetBase == "" {
+ gadgetBase = "core"
+ }
+ // Sanity check
+ // TODO: do we want to relax this? the new logic would allow
+ // but it might just be confusing for now
+ if baseSnap != "" && gadgetBase != baseSnap {
+ return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base())
+ }
+ if _, err = addEssential(gadgetBase); err != nil {
+ return err
+ }
+ }
+
+ s.essentialSnapsNum = len(s.snaps)
+
+ // the rest of the snaps
+ for _, sn := range yamlSnaps {
+ if added[sn.Name] {
+ continue
+ }
+ seedSnap, err := s.addSnap(sn, tm)
+ if err != nil {
+ return err
+ }
+ if required.Contains(seedSnap) {
+ seedSnap.Required = true
+ }
+ }
+
+ return nil
+}
+
+func (s *seed16) UsesSnapdSnap() bool {
+ return s.usesSnapdSnap
+}
+
+func (s *seed16) EssentialSnaps() []*Snap {
+ return s.snaps[:s.essentialSnapsNum]
+}
+
+func (s *seed16) ModeSnaps(mode string) ([]*Snap, error) {
+ if mode != "run" {
+ return nil, fmt.Errorf("internal error: Core 16/18 have only run mode, got: %s", mode)
+ }
+ return s.snaps[s.essentialSnapsNum:], nil
+}
diff --git a/seed/seed16_test.go b/seed/seed16_test.go
new file mode 100644
index 0000000000..a916e76d57
--- /dev/null
+++ b/seed/seed16_test.go
@@ -0,0 +1,1058 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package seed_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ . "gopkg.in/check.v1"
+ "gopkg.in/yaml.v2"
+
+ "github.com/snapcore/snapd/asserts"
+ "github.com/snapcore/snapd/asserts/assertstest"
+ "github.com/snapcore/snapd/seed"
+ "github.com/snapcore/snapd/seed/seedtest"
+ "github.com/snapcore/snapd/snap"
+ "github.com/snapcore/snapd/snap/snaptest"
+ "github.com/snapcore/snapd/testutil"
+ "github.com/snapcore/snapd/timings"
+)
+
+type seed16Suite struct {
+ testutil.BaseTest
+
+ *seedtest.TestingSeed
+ devAcct *asserts.Account
+
+ seedDir string
+
+ seed16 seed.Seed
+
+ db *asserts.Database
+
+ perfTimings timings.Measurer
+}
+
+var _ = Suite(&seed16Suite{})
+
+var (
+ brandPrivKey, _ = assertstest.GenerateKey(752)
+)
+
+func (s *seed16Suite) SetUpTest(c *C) {
+ s.BaseTest.SetUpTest(c)
+ s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
+
+ s.TestingSeed = &seedtest.TestingSeed{}
+ s.SetupAssertSigning("canonical", s)
+ s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{
+ "verification": "verified",
+ })
+
+ s.seedDir = c.MkDir()
+
+ s.SnapsDir = filepath.Join(s.seedDir, "snaps")
+ s.AssertsDir = filepath.Join(s.seedDir, "assertions")
+
+ s.devAcct = assertstest.NewAccount(s.StoreSigning, "developer", map[string]interface{}{
+ "account-id": "developerid",
+ }, "")
+ assertstest.AddMany(s.StoreSigning, s.devAcct)
+
+ seed16, err := seed.Open(s.seedDir)
+ c.Assert(err, IsNil)
+ s.seed16 = seed16
+
+ db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
+ Backstore: asserts.NewMemoryBackstore(),
+ Trusted: s.StoreSigning.Trusted,
+ })
+ c.Assert(err, IsNil)
+ s.db = db
+
+ s.perfTimings = timings.New(nil)
+}
+
+func (s *seed16Suite) commitTo(b *asserts.Batch) error {
+ return b.CommitTo(s.db, nil)
+}
+
+func (s *seed16Suite) TestLoadAssertionsNoAssertions(c *C) {
+ c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), Equals, seed.ErrNoAssertions)
+}
+
+func (s *seed16Suite) TestLoadAssertionsNoModelAssertion(c *C) {
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "seed must have a model assertion")
+}
+
+func (s *seed16Suite) TestLoadAssertionsTwoModelAssertionsError(c *C) {
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ headers := map[string]interface{}{
+ "architecture": "amd64",
+ "kernel": "pc-kernel",
+ "gadget": "pc",
+ }
+ modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
+ s.WriteAssertions("model.asserts", modelChain...)
+ modelChain = s.MakeModelAssertionChain("my-brand", "my-model-2", headers)
+ s.WriteAssertions("model2.asserts", modelChain...)
+
+ c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "cannot have multiple model assertions in seed")
+}
+
+func (s *seed16Suite) TestLoadAssertionsConsistencyError(c *C) {
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ // write out only the model assertion
+ headers := map[string]interface{}{
+ "architecture": "amd64",
+ "kernel": "pc-kernel",
+ "gadget": "pc",
+ }
+ model := s.Brands.Model("my-brand", "my-model", headers)
+ s.WriteAssertions("model.asserts", model)
+
+ c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "cannot resolve prerequisite assertion: account-key .*")
+}
+
+func (s *seed16Suite) TestLoadAssertionsModelHappy(c *C) {
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ headers := map[string]interface{}{
+ "architecture": "amd64",
+ "kernel": "pc-kernel",
+ "gadget": "pc",
+ }
+ modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
+ s.WriteAssertions("model.asserts", modelChain...)
+
+ err = s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ model, err := s.seed16.Model()
+ c.Assert(err, IsNil)
+ c.Check(model.Model(), Equals, "my-model")
+
+ _, err = s.db.Find(asserts.ModelType, map[string]string{
+ "series": "16",
+ "brand-id": "my-brand",
+ "model": "my-model",
+ })
+ c.Assert(err, IsNil)
+}
+
+func (s *seed16Suite) TestLoadAssertionsModelTempDBHappy(c *C) {
+ r := seed.MockTrusted(s.StoreSigning.Trusted)
+ defer r()
+
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ headers := map[string]interface{}{
+ "architecture": "amd64",
+ "kernel": "pc-kernel",
+ "gadget": "pc",
+ }
+ modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
+ s.WriteAssertions("model.asserts", modelChain...)
+
+ err = s.seed16.LoadAssertions(nil, nil)
+ c.Assert(err, IsNil)
+
+ model, err := s.seed16.Model()
+ c.Assert(err, IsNil)
+ c.Check(model.Model(), Equals, "my-model")
+}
+
+func (s *seed16Suite) TestSkippedLoadAssertion(c *C) {
+ _, err := s.seed16.Model()
+ c.Check(err, ErrorMatches, "internal error: model assertion unset")
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Check(err, ErrorMatches, "internal error: model assertion unset")
+}
+
+func (s *seed16Suite) TestLoadMetaNoMeta(c *C) {
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ headers := map[string]interface{}{
+ "architecture": "amd64",
+ "kernel": "pc-kernel",
+ "gadget": "pc",
+ }
+ modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
+ s.WriteAssertions("model.asserts", modelChain...)
+
+ err = s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Check(err, Equals, seed.ErrNoMeta)
+}
+
+func (s *seed16Suite) TestLoadMetaInvalidSeedYaml(c *C) {
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ headers := map[string]interface{}{
+ "architecture": "amd64",
+ "kernel": "pc-kernel",
+ "gadget": "pc",
+ }
+ modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
+ s.WriteAssertions("model.asserts", modelChain...)
+
+ err = s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ // create a seed.yaml
+ content, err := yaml.Marshal(map[string]interface{}{
+ "snaps": []*seed.Snap16{{
+ Name: "core",
+ Channel: "track/not-a-risk",
+ }},
+ })
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(s.seedDir, "seed.yaml"), content, 0644)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Check(err, ErrorMatches, `cannot read seed yaml: invalid risk in channel name: track/not-a-risk`)
+}
+
+var snapYaml = map[string]string{
+ "core": `name: core
+type: os
+version: 1.0
+`,
+ "pc-kernel": `name: pc-kernel
+type: kernel
+version: 1.0
+`,
+ "pc": `name: pc
+type: gadget
+version: 1.0
+`,
+ "required": `name: required
+type: app
+version: 1.0
+`,
+ "snapd": `name: snapd
+type: snapd
+version: 1.0
+`,
+ "core18": `name: core18
+type: base
+version: 1.0
+`,
+ "pc-kernel=18": `name: pc-kernel
+type: kernel
+version: 1.0
+`,
+ "pc=18": `name: pc
+type: gadget
+base: core18
+version: 1.0
+`,
+ "required18": `name: required18
+type: app
+base: core18
+version: 1.0
+`,
+ "classic-snap": `name: classic-snap
+type: app
+confinement: classic
+version: 1.0
+`,
+ "classic-gadget": `name: classic-gadget
+type: gadget
+version: 1.0
+`,
+ "classic-gadget18": `name: classic-gadget18
+type: gadget
+base: core18
+version: 1.0
+`,
+ "private-snap": `name: private-snap
+base: core18
+version: 1.0
+`,
+ "contactable-snap": `name: contactable-snap
+base: core18
+version: 1.0
+`,
+}
+
+var snapPublishers = map[string]string{
+ "required": "developerid",
+}
+
+var (
+ coreSeed = &seed.Snap16{
+ Name: "core",
+ Channel: "stable",
+ }
+ kernelSeed = &seed.Snap16{
+ Name: "pc-kernel",
+ Channel: "stable",
+ }
+ gadgetSeed = &seed.Snap16{
+ Name: "pc",
+ Channel: "stable",
+ }
+ requiredSeed = &seed.Snap16{
+ Name: "required",
+ Channel: "stable",
+ }
+ // Core 18
+ snapdSeed = &seed.Snap16{
+ Name: "snapd",
+ Channel: "stable",
+ }
+ core18Seed = &seed.Snap16{
+ Name: "core18",
+ Channel: "stable",
+ }
+ kernel18Seed = &seed.Snap16{
+ Name: "pc-kernel",
+ Channel: "18",
+ }
+ gadget18Seed = &seed.Snap16{
+ Name: "pc",
+ Channel: "18",
+ }
+ required18Seed = &seed.Snap16{
+ Name: "required18",
+ Channel: "stable",
+ }
+ classicSnapSeed = &seed.Snap16{
+ Name: "classic-snap",
+ Channel: "stable",
+ Classic: true,
+ }
+ classicGadgetSeed = &seed.Snap16{
+ Name: "classic-gadget",
+ Channel: "stable",
+ }
+ classicGadget18Seed = &seed.Snap16{
+ Name: "classic-gadget18",
+ Channel: "stable",
+ }
+ privateSnapSeed = &seed.Snap16{
+ Name: "private-snap",
+ Channel: "stable",
+ Private: true,
+ }
+ contactableSnapSeed = &seed.Snap16{
+ Name: "contactable-snap",
+ Channel: "stable",
+ Contact: "author@example.com",
+ }
+)
+
+func (s *seed16Suite) makeSeed(c *C, modelHeaders map[string]interface{}, seedSnaps ...*seed.Snap16) []*seed.Snap16 {
+ coreHeaders := map[string]interface{}{
+ "architecture": "amd64",
+ }
+
+ if _, ok := modelHeaders["classic"]; !ok {
+ coreHeaders["kernel"] = "pc-kernel"
+ coreHeaders["gadget"] = "pc"
+ }
+
+ err := os.Mkdir(s.AssertsDir, 0755)
+ c.Assert(err, IsNil)
+
+ modelChain := s.MakeModelAssertionChain("my-brand", "my-model", coreHeaders, modelHeaders)
+ s.WriteAssertions("model.asserts", modelChain...)
+
+ err = os.Mkdir(s.SnapsDir, 0755)
+ c.Assert(err, IsNil)
+
+ var completeSeedSnaps []*seed.Snap16
+ for _, seedSnap := range seedSnaps {
+ completeSeedSnap := *seedSnap
+ var snapFname string
+ if seedSnap.Unasserted {
+ mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml[seedSnap.Name], nil)
+ snapFname = filepath.Base(mockSnapFile)
+ err := os.Rename(mockSnapFile, filepath.Join(s.seedDir, "snaps", snapFname))
+ c.Assert(err, IsNil)
+ } else {
+ publisher := snapPublishers[seedSnap.Name]
+ if publisher == "" {
+ publisher = "canonical"
+ }
+ whichYaml := seedSnap.Name
+ if seedSnap.Channel != "stable" {
+ whichYaml = whichYaml + "=" + seedSnap.Channel
+ }
+ fname, decl, rev := s.MakeAssertedSnap(c, snapYaml[whichYaml], nil, snap.R(1), publisher)
+ acct, err := s.StoreSigning.Find(asserts.AccountType, map[string]string{"account-id": publisher})
+ c.Assert(err, IsNil)
+ s.WriteAssertions(fmt.Sprintf("%s.asserts", seedSnap.Name), rev, decl, acct)
+ snapFname = fname
+ }
+ completeSeedSnap.File = snapFname
+ completeSeedSnaps = append(completeSeedSnaps, &completeSeedSnap)
+ }
+
+ // create a seed.yaml
+ content, err := yaml.Marshal(map[string]interface{}{
+ "snaps": completeSeedSnaps,
+ })
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(s.seedDir, "seed.yaml"), content, 0644)
+ c.Assert(err, IsNil)
+
+ return completeSeedSnaps
+}
+
+func (s *seed16Suite) expectedPath(snapName string) string {
+ return filepath.Join(s.seedDir, "snaps", filepath.Base(s.AssertedSnap(snapName)))
+}
+
+func (s *seed16Suite) TestLoadMetaCore16Minimal(c *C) {
+ s.makeSeed(c, nil, coreSeed, kernelSeed, gadgetSeed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 3)
+
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("core"),
+ SideInfo: &s.AssertedSnapInfo("core").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("pc-kernel"),
+ SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("pc"),
+ SideInfo: &s.AssertedSnapInfo("pc").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 0)
+}
+
+func (s *seed16Suite) TestLoadMetaCore16(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "required-snaps": []interface{}{"required"},
+ }, coreSeed, kernelSeed, gadgetSeed, requiredSeed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 3)
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 1)
+
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("required"),
+ SideInfo: &s.AssertedSnapInfo("required").SideInfo,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaCore18Minimal(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "base": "core18",
+ "kernel": "pc-kernel=18",
+ "gadget": "pc=18",
+ }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 4)
+
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("snapd"),
+ SideInfo: &s.AssertedSnapInfo("snapd").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("core18"),
+ SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("pc-kernel"),
+ SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "18",
+ }, {
+ Path: s.expectedPath("pc"),
+ SideInfo: &s.AssertedSnapInfo("pc").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "18",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 0)
+}
+
+func (s *seed16Suite) TestLoadMetaCore18(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "base": "core18",
+ "kernel": "pc-kernel=18",
+ "gadget": "pc=18",
+ "required-snaps": []interface{}{"core", "required", "required18"},
+ }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, requiredSeed, coreSeed, required18Seed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 4)
+
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("snapd"),
+ SideInfo: &s.AssertedSnapInfo("snapd").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("core18"),
+ SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("pc-kernel"),
+ SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "18",
+ }, {
+ Path: s.expectedPath("pc"),
+ SideInfo: &s.AssertedSnapInfo("pc").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "18",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 3)
+
+ // these are not sorted by type, firstboot will do that
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("required"),
+ SideInfo: &s.AssertedSnapInfo("required").SideInfo,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("core"),
+ SideInfo: &s.AssertedSnapInfo("core").SideInfo,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("required18"),
+ SideInfo: &s.AssertedSnapInfo("required18").SideInfo,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaClassicNothing(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "classic": "true",
+ })
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 0)
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 0)
+}
+
+func (s *seed16Suite) TestLoadMetaClassicCore(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "classic": "true",
+ }, coreSeed, classicSnapSeed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 1)
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("core"),
+ SideInfo: &s.AssertedSnapInfo("core").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+
+ // classic-snap is not required, just an extra snap
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 1)
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("classic-snap"),
+ SideInfo: &s.AssertedSnapInfo("classic-snap").SideInfo,
+ Channel: "stable",
+ Classic: true,
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaClassicCoreWithGadget(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "classic": "true",
+ "gadget": "classic-gadget",
+ }, coreSeed, classicGadgetSeed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 2)
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("core"),
+ SideInfo: &s.AssertedSnapInfo("core").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ {
+ Path: s.expectedPath("classic-gadget"),
+ SideInfo: &s.AssertedSnapInfo("classic-gadget").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 0)
+}
+
+func (s *seed16Suite) TestLoadMetaClassicSnapd(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "classic": "true",
+ "required-snaps": []interface{}{"core18", "required18"},
+ }, snapdSeed, core18Seed, required18Seed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 1)
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("snapd"),
+ SideInfo: &s.AssertedSnapInfo("snapd").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 2)
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("core18"),
+ SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("required18"),
+ SideInfo: &s.AssertedSnapInfo("required18").SideInfo,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "classic": "true",
+ "gadget": "classic-gadget",
+ }, snapdSeed, classicGadgetSeed, coreSeed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 3)
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("snapd"),
+ SideInfo: &s.AssertedSnapInfo("snapd").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("classic-gadget"),
+ SideInfo: &s.AssertedSnapInfo("classic-gadget").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("core"),
+ SideInfo: &s.AssertedSnapInfo("core").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 0)
+}
+
+func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget18(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "classic": "true",
+ "gadget": "classic-gadget18",
+ "required-snaps": []interface{}{"core", "required"},
+ }, snapdSeed, coreSeed, requiredSeed, classicGadget18Seed, core18Seed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 3)
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("snapd"),
+ SideInfo: &s.AssertedSnapInfo("snapd").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("classic-gadget18"),
+ SideInfo: &s.AssertedSnapInfo("classic-gadget18").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("core18"),
+ SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 2)
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("core"),
+ SideInfo: &s.AssertedSnapInfo("core").SideInfo,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("required"),
+ SideInfo: &s.AssertedSnapInfo("required").SideInfo,
+ Required: true,
+ Channel: "stable",
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaCore18Local(c *C) {
+ localRequired18Seed := &seed.Snap16{
+ Name: "required18",
+ Unasserted: true,
+ DevMode: true,
+ }
+ s.makeSeed(c, map[string]interface{}{
+ "base": "core18",
+ "kernel": "pc-kernel=18",
+ "gadget": "pc=18",
+ "required-snaps": []interface{}{"core", "required18"},
+ }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, localRequired18Seed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 4)
+
+ c.Check(essSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("snapd"),
+ SideInfo: &s.AssertedSnapInfo("snapd").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("core18"),
+ SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("pc-kernel"),
+ SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "18",
+ }, {
+ Path: s.expectedPath("pc"),
+ SideInfo: &s.AssertedSnapInfo("pc").SideInfo,
+ Essential: true,
+ Required: true,
+ Channel: "18",
+ },
+ })
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 1)
+
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: filepath.Join(s.seedDir, "snaps", "required18_1.0_all.snap"),
+ SideInfo: &snap.SideInfo{RealName: "required18"},
+ Required: true,
+ DevMode: true,
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaCore18StoreInfo(c *C) {
+ s.makeSeed(c, map[string]interface{}{
+ "base": "core18",
+ "kernel": "pc-kernel=18",
+ "gadget": "pc=18",
+ }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, privateSnapSeed, contactableSnapSeed)
+
+ err := s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ err = s.seed16.LoadMeta(s.perfTimings)
+ c.Assert(err, IsNil)
+
+ essSnaps := s.seed16.EssentialSnaps()
+ c.Check(essSnaps, HasLen, 4)
+
+ runSnaps, err := s.seed16.ModeSnaps("run")
+ c.Assert(err, IsNil)
+ c.Check(runSnaps, HasLen, 2)
+
+ privateSnapSideInfo := s.AssertedSnapInfo("private-snap").SideInfo
+ privateSnapSideInfo.Private = true
+ contactableSnapSideInfo := s.AssertedSnapInfo("contactable-snap").SideInfo
+ contactableSnapSideInfo.Contact = "author@example.com"
+
+ // these are not sorted by type, firstboot will do that
+ c.Check(runSnaps, DeepEquals, []*seed.Snap{
+ {
+ Path: s.expectedPath("private-snap"),
+ SideInfo: &privateSnapSideInfo,
+ Channel: "stable",
+ }, {
+ Path: s.expectedPath("contactable-snap"),
+ SideInfo: &contactableSnapSideInfo,
+ Channel: "stable",
+ },
+ })
+}
+
+func (s *seed16Suite) TestLoadMetaBrokenSeed(c *C) {
+ seedSnap16s := s.makeSeed(c, map[string]interface{}{
+ "base": "core18",
+ "kernel": "pc-kernel=18",
+ "gadget": "pc=18",
+ "required-snaps": []interface{}{"required18"},
+ }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, required18Seed)
+
+ otherSnapFile := snaptest.MakeTestSnapWithFiles(c, `name: other
+version: other`, nil)
+ otherFname := filepath.Base(otherSnapFile)
+ err := os.Rename(otherSnapFile, filepath.Join(s.seedDir, "snaps", otherFname))
+ c.Assert(err, IsNil)
+
+ const otherBaseGadget = `name: pc
+type: gadget
+base: other-base
+version: other-base
+`
+ otherBaseGadgetFname, obgDecl, obgRev := s.MakeAssertedSnap(c, otherBaseGadget, nil, snap.R(3), "canonical")
+ s.WriteAssertions("other-gadget.asserts", obgDecl, obgRev)
+
+ err = s.seed16.LoadAssertions(s.db, s.commitTo)
+ c.Assert(err, IsNil)
+
+ omit := func(which int) func([]*seed.Snap16) []*seed.Snap16 {
+ return func(snaps []*seed.Snap16) []*seed.Snap16 {
+ broken := make([]*seed.Snap16, 0, len(snaps)-1)
+ for i, sn := range snaps {
+ if i == which {
+ continue
+ }
+ broken = append(broken, sn)
+ }
+ return broken
+ }
+ }
+ replaceFile := func(snapName, fname string) func([]*seed.Snap16) []*seed.Snap16 {
+ return func(snaps []*seed.Snap16) []*seed.Snap16 {
+ for i := range snaps {
+ if snaps[i].Name != snapName {
+ continue
+ }
+ sn := *snaps[i]
+ sn.File = fname
+ snaps[i] = &sn
+ }
+ return snaps
+ }
+ }
+
+ tests := []struct {
+ breakSeed func([]*seed.Snap16) []*seed.Snap16
+ err string
+ }{
+ {omit(0), `essential snap "snapd" required by the model is missing in the seed`},
+ {omit(1), `essential snap "core18" required by the model is missing in the seed`},
+ {omit(2), `essential snap "pc-kernel" required by the model is missing in the seed`},
+ {omit(3), `essential snap "pc" required by the model is missing in the seed`},
+ // omitting "required18" currently doesn't error in any way
+ {replaceFile("core18", otherFname), `cannot find signatures with metadata for snap "core18".*`},
+ {replaceFile("required18", otherFname), `cannot find signatures with metadata for snap "required18".*`},
+ {replaceFile("core18", "not-existent"), `cannot compute snap .* digest: .*`},
+ {replaceFile("pc", otherBaseGadgetFname), `cannot use gadget snap because its base "other-base" is different from model base "core18"`},
+ }
+
+ for _, t := range tests {
+ testSeedSnap16s := make([]*seed.Snap16, 5)
+ copy(testSeedSnap16s, seedSnap16s)
+
+ testSeedSnap16s = t.breakSeed(testSeedSnap16s)
+ content, err := yaml.Marshal(map[string]interface{}{
+ "snaps": testSeedSnap16s,
+ })
+ c.Assert(err, IsNil)
+ err = ioutil.WriteFile(filepath.Join(s.seedDir, "seed.yaml"), content, 0644)
+ c.Assert(err, IsNil)
+
+ c.Check(s.seed16.LoadMeta(s.perfTimings), ErrorMatches, t.err)
+ }
+}
diff --git a/seed/seed_yaml.go b/seed/seed_yaml.go
index 166b00a848..db24e393f4 100644
--- a/seed/seed_yaml.go
+++ b/seed/seed_yaml.go
@@ -34,7 +34,8 @@ import (
// Snap points to a snap in the seed to install, together with
// assertions (or alone if unasserted is true) it will be used to
// drive the installation and ultimately set SideInfo/SnapState for it.
-type Snap struct {
+// TODO: make this internal
+type Snap16 struct {
Name string `yaml:"name"`
// cross-reference/audit
@@ -55,11 +56,13 @@ type Snap struct {
File string `yaml:"file"`
}
-type Seed struct {
- Snaps []*Snap `yaml:"snaps"`
+// TODO: make all of this internal only
+
+type Seed16 struct {
+ Snaps []*Snap16 `yaml:"snaps"`
}
-func ReadYaml(fn string) (*Seed, error) {
+func ReadYaml(fn string) (*Seed16, error) {
errPrefix := "cannot read seed yaml"
yamlData, err := ioutil.ReadFile(fn)
@@ -67,7 +70,7 @@ func ReadYaml(fn string) (*Seed, error) {
return nil, fmt.Errorf("%s: %v", errPrefix, err)
}
- var seed Seed
+ var seed Seed16
if err := yaml.Unmarshal(yamlData, &seed); err != nil {
return nil, fmt.Errorf("%s: cannot unmarshal %q: %s", errPrefix, yamlData, err)
}
@@ -105,7 +108,7 @@ func ReadYaml(fn string) (*Seed, error) {
return &seed, nil
}
-func (seed *Seed) Write(seedFn string) error {
+func (seed *Seed16) Write(seedFn string) error {
data, err := yaml.Marshal(&seed)
if err != nil {
return err
diff --git a/seed/seed_yaml_test.go b/seed/seed_yaml_test.go
index 0ff68547be..ee9ec65eda 100644
--- a/seed/seed_yaml_test.go
+++ b/seed/seed_yaml_test.go
@@ -55,7 +55,7 @@ func (s *seedYamlTestSuite) TestSimple(c *C) {
seedYaml, err := seed.ReadYaml(fn)
c.Assert(err, IsNil)
c.Assert(seedYaml.Snaps, HasLen, 2)
- c.Assert(seedYaml.Snaps[0], DeepEquals, &seed.Snap{
+ c.Assert(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{
File: "foo_1.0_all.snap",
Name: "foo",
SnapID: "snapidsnapidsnapid",
@@ -63,7 +63,7 @@ func (s *seedYamlTestSuite) TestSimple(c *C) {
Channel: "stable",
DevMode: true,
})
- c.Assert(seedYaml.Snaps[1], DeepEquals, &seed.Snap{
+ c.Assert(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{
File: "local.snap",
Name: "local",
Unasserted: true,
diff --git a/seed/seedtest/seedtest.go b/seed/seedtest/seedtest.go
index a2ea4e9300..4cc4b2db74 100644
--- a/seed/seedtest/seedtest.go
+++ b/seed/seedtest/seedtest.go
@@ -112,6 +112,7 @@ func (ss *SeedSnaps) MakeAssertedSnap(c *C, snapYaml string, files [][]string, r
}
ss.snaps[snapName] = snapFile
+ info.SideInfo.RealName = snapName
ss.infos[snapName] = info
return declA.(*asserts.SnapDeclaration), revA.(*asserts.SnapRevision)
diff --git a/tests/lib/bin/mountinfo-tool b/tests/lib/bin/mountinfo-tool
index ec51733572..53a7297846 100755
--- a/tests/lib/bin/mountinfo-tool
+++ b/tests/lib/bin/mountinfo-tool
@@ -94,6 +94,9 @@ class MountInfoEntry(object):
self.fs_type = ""
self.mount_source = ""
self.sb_opts = ""
+ # This field does not represent kernel state.
+ # It is a marker for an entry being matched by a filter.
+ self.matched = False
def __eq__(self, other):
# type: (object) -> Union[NotImplemented, bool]
@@ -687,6 +690,13 @@ The exit code indicates if any mount point matched the query.
raise SystemExit(exc)
entries = [MountInfoEntry.parse(line) for line in opts.file]
+ # Apply entry filtering ahead of any renumbering.
+ num_matched = 0
+ for e in entries:
+ if matches(e, filters):
+ e.matched = True
+ num_matched += 1
+
# Build rewrite state based on reference tables. This way the entries
# we will display can be correlated to other tables.
rs = RewriteState()
@@ -725,9 +735,6 @@ The exit code indicates if any mount point matched the query.
if opts.rename:
rewrite_rename(entries, rewrite_order, rs)
- # Apply entry filtering.
- entries = [e for e in entries if matches(e, filters)]
-
# Apply entry reordering for display.
if opts.display_order:
@@ -737,6 +744,8 @@ The exit code indicates if any mount point matched the query.
entries.sort(key=display_key_fn)
for e in entries:
+ if not e.matched:
+ continue
if attrs:
values = [] # type: List[Any]
for attr in attrs:
@@ -747,15 +756,16 @@ The exit code indicates if any mount point matched the query.
print(*values)
else:
print(e)
- if opts.one and len(entries) != 1:
+ if opts.one and num_matched != 1:
raise SystemExit(
- "--one requires exactly one match, found {}".format(len(entries))
+ "--one requires exactly one match, found {}".format(num_matched)
)
- # Return with an exit code indicating if anything matched.
- # This allows mountinfo-tool to be used in scripts.
- if len(entries) == 0:
+ # Return with an exit code indicating if anything matched.
+ # This allows mountinfo-tool to be used in scripts.
+ if num_matched == 0:
raise SystemExit(1)
+
class MountInfoEntryTests(unittest.TestCase):
non_zero_values = {
diff --git a/tests/lib/boot.sh b/tests/lib/boot.sh
index 475ceccdf8..282f1b26f3 100644
--- a/tests/lib/boot.sh
+++ b/tests/lib/boot.sh
@@ -1,6 +1,7 @@
#!/bin/bash
GRUB_EDITENV=grub-editenv
+GRUBENV_FILE=/boot/grub/grubenv
case "$SPREAD_SYSTEM" in
fedora-*|opensuse-*|amazon-*|centos-*)
GRUB_EDITENV=grub2-editenv
@@ -11,12 +12,16 @@ bootenv() {
if [ $# -eq 0 ]; then
if command -v "$GRUB_EDITENV" >/dev/null; then
"$GRUB_EDITENV" list
+ elif [ -s "$GRUBENV_FILE" ]; then
+ cat "$GRUBENV_FILE"
else
fw_printenv
fi
else
if command -v "$GRUB_EDITENV" >/dev/null; then
"$GRUB_EDITENV" list | grep "^$1"
+ elif [ -s "$GRUBENV_FILE" ]; then
+ grep "^$1" "$GRUBENV_FILE"
else
fw_printenv "$1"
fi | sed "s/^${1}=//"
@@ -29,6 +34,8 @@ bootenv_unset() {
if command -v "$GRUB_EDITENV" >/dev/null; then
"$GRUB_EDITENV" /boot/grub/grubenv unset "$var"
+ elif [ -s "$GRUBENV_FILE" ]; then
+ sed -i "/^$var=/d" "$GRUBENV_FILE"
else
fw_setenv "$var"
fi
diff --git a/tests/main/mount-ns/google.ubuntu-core-16-64/PER-SNAP-16.expected.txt b/tests/main/mount-ns/google.ubuntu-core-16-64/PER-SNAP-16.expected.txt
index 6a6e2da177..246d87e3a6 100644
--- a/tests/main/mount-ns/google.ubuntu-core-16-64/PER-SNAP-16.expected.txt
+++ b/tests/main/mount-ns/google.ubuntu-core-16-64/PER-SNAP-16.expected.txt
@@ -69,160 +69,152 @@
1069 1065 2:10 /snapd/ns /run/snapd/ns rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=VARIABLE,mode=755
1070 1065 2:13 / /run/user/0 rw,nosuid,nodev,relatime master:59 - tmpfs tmpfs rw,size=VARIABLE,mode=700
1071 1001 1:1 /system-data/snap /snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1072 1071 1:1 /system-data/snap /snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1073 1071 0:2 / /snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-1074 1072 0:2 / /snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-1075 1071 0:3 / /snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-1076 1072 0:3 / /snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-1077 1071 0:4 / /snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-1078 1072 0:4 / /snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-1079 1071 0:5 / /snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-1080 1072 0:5 / /snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-1081 1071 0:6 / /snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-1082 1072 0:6 / /snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-1083 1071 0:7 / /snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-1084 1072 0:7 / /snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-1085 1071 0:8 / /snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-1086 1072 0:8 / /snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-1087 1001 2:14 / /sys rw,nosuid,nodev,noexec,relatime master:67 - sysfs sysfs rw
-1088 1087 2:15 / /sys/fs/cgroup rw master:68 - tmpfs tmpfs rw,mode=755
-1089 1088 2:16 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime master:69 - cgroup cgroup rw,blkio
-1090 1088 2:17 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime master:70 - cgroup cgroup rw,cpu,cpuacct
-1091 1088 2:18 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime master:71 - cgroup cgroup rw,cpuset,clone_children
-1092 1088 2:19 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime master:72 - cgroup cgroup rw,devices
-1093 1088 2:20 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime master:73 - cgroup cgroup rw,freezer
-1094 1088 2:21 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime master:74 - cgroup cgroup rw,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb
-1095 1088 2:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime master:75 - cgroup cgroup rw,memory
-1096 1088 2:23 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime master:76 - cgroup cgroup rw,net_cls,net_prio
-1097 1088 2:24 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime master:77 - cgroup cgroup rw,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event
-1098 1088 2:25 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime master:78 - cgroup cgroup rw,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids
-1099 1088 2:26 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime master:79 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
-1100 1087 2:27 / /sys/fs/fuse/connections rw,relatime master:80 - fusectl fusectl rw
-1101 1087 2:28 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime master:81 - pstore pstore rw
-1102 1087 2:29 / /sys/kernel/debug rw,relatime master:82 - debugfs debugfs rw
-1103 1087 2:30 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime master:83 - securityfs securityfs rw
-1104 1001 2:31 / /tmp rw,relatime master:84 - tmpfs tmpfs rw
-1105 1104 2:31 /snap.test-snapd-mountinfo-core16/tmp /tmp rw,relatime - tmpfs tmpfs rw
-1106 1001 1:1 /system-data/var/cache/apparmor /var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1107 1001 1:1 /system-data/var/cache/snapd /var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1108 1001 1:1 /system-data/var/lib/apparmor /var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1109 1001 1:1 /system-data/var/lib/cloud /var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1110 1001 1:1 /system-data/var/lib/console-conf /var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1111 1001 1:1 /system-data/var/lib/dbus /var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1112 1001 1:1 /system-data/var/lib/dhcp /var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1113 1001 1:1 /system-data/var/lib/extrausers /var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1114 1001 1:1 /system-data/var/lib/initramfs-tools /var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1115 1001 1:1 /system-data/var/lib/logrotate /var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1116 1001 1:1 /system-data/var/lib/misc /var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1117 1001 1:1 /system-data/var/lib/snapd /var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1119 1118 0:0 / /var/lib/snapd/hostfs ro,relatime master:1 - squashfs /dev/loop0 ro
-1118 1117 1:1 /system-data/var/lib/snapd/hostfs /var/lib/snapd/hostfs rw,relatime - ext4 /dev/sda3 rw,data=ordered
-1120 1119 1:0 / /var/lib/snapd/hostfs/boot/efi rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
-1121 1119 1:0 /EFI/ubuntu /var/lib/snapd/hostfs/boot/grub rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
-1122 1119 1:1 /system-data/etc/apparmor.d/cache /var/lib/snapd/hostfs/etc/apparmor.d/cache rw,relatime master:8 - ext4 /dev/sda3 rw,data=ordered
-1123 1119 1:1 /system-data/etc/cloud /var/lib/snapd/hostfs/etc/cloud rw,relatime master:9 - ext4 /dev/sda3 rw,data=ordered
-1124 1119 1:1 /system-data/etc/dbus-1/system.d /var/lib/snapd/hostfs/etc/dbus-1/system.d rw,relatime master:10 - ext4 /dev/sda3 rw,data=ordered
-1125 1119 1:1 /system-data/etc/default/keyboard /var/lib/snapd/hostfs/etc/default/keyboard rw,relatime master:11 - ext4 /dev/sda3 rw,data=ordered
-1126 1119 1:1 /system-data/etc/default/swapfile /var/lib/snapd/hostfs/etc/default/swapfile rw,relatime master:12 - ext4 /dev/sda3 rw,data=ordered
-1127 1119 1:1 /system-data/etc/environment /var/lib/snapd/hostfs/etc/environment rw,relatime master:13 - ext4 /dev/sda3 rw,data=ordered
-1128 1119 2:5 /image.fstab /var/lib/snapd/hostfs/etc/fstab rw,nosuid,noexec,relatime master:14 - tmpfs tmpfs rw,mode=755
-1129 1119 1:1 /system-data/root/test-etc/group /var/lib/snapd/hostfs/etc/group ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1130 1119 1:1 /system-data/root/test-etc/gshadow /var/lib/snapd/hostfs/etc/gshadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1131 1119 1:1 /system-data/etc/hosts /var/lib/snapd/hostfs/etc/hosts rw,relatime master:16 - ext4 /dev/sda3 rw,data=ordered
-1132 1119 1:1 /system-data/etc/init /var/lib/snapd/hostfs/etc/init rw,relatime master:17 - ext4 /dev/sda3 rw,data=ordered
-1133 1119 1:1 /system-data/etc/init.d /var/lib/snapd/hostfs/etc/init.d rw,relatime master:18 - ext4 /dev/sda3 rw,data=ordered
-1134 1119 1:1 /system-data/etc/iproute2 /var/lib/snapd/hostfs/etc/iproute2 rw,relatime master:19 - ext4 /dev/sda3 rw,data=ordered
-1135 1119 1:1 /system-data/etc/machine-id /var/lib/snapd/hostfs/etc/machine-id rw,relatime master:20 - ext4 /dev/sda3 rw,data=ordered
-1136 1119 1:1 /system-data/etc/modprobe.d /var/lib/snapd/hostfs/etc/modprobe.d rw,relatime master:21 - ext4 /dev/sda3 rw,data=ordered
-1137 1119 1:1 /system-data/etc/modules-load.d /var/lib/snapd/hostfs/etc/modules-load.d rw,relatime master:22 - ext4 /dev/sda3 rw,data=ordered
-1138 1119 1:1 /system-data/etc/netplan /var/lib/snapd/hostfs/etc/netplan rw,relatime master:23 - ext4 /dev/sda3 rw,data=ordered
-1139 1119 1:1 /system-data/etc/network/if-up.d /var/lib/snapd/hostfs/etc/network/if-up.d rw,relatime master:24 - ext4 /dev/sda3 rw,data=ordered
-1140 1119 1:1 /system-data/etc/network/interfaces.d /var/lib/snapd/hostfs/etc/network/interfaces.d rw,relatime master:25 - ext4 /dev/sda3 rw,data=ordered
-1141 1119 1:1 /system-data/root/test-etc/passwd /var/lib/snapd/hostfs/etc/passwd ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1142 1119 1:1 /system-data/etc/ppp /var/lib/snapd/hostfs/etc/ppp rw,relatime master:26 - ext4 /dev/sda3 rw,data=ordered
-1143 1119 1:1 /system-data/etc/rc0.d /var/lib/snapd/hostfs/etc/rc0.d rw,relatime master:27 - ext4 /dev/sda3 rw,data=ordered
-1144 1119 1:1 /system-data/etc/rc1.d /var/lib/snapd/hostfs/etc/rc1.d rw,relatime master:28 - ext4 /dev/sda3 rw,data=ordered
-1145 1119 1:1 /system-data/etc/rc2.d /var/lib/snapd/hostfs/etc/rc2.d rw,relatime master:29 - ext4 /dev/sda3 rw,data=ordered
-1146 1119 1:1 /system-data/etc/rc3.d /var/lib/snapd/hostfs/etc/rc3.d rw,relatime master:30 - ext4 /dev/sda3 rw,data=ordered
-1147 1119 1:1 /system-data/etc/rc4.d /var/lib/snapd/hostfs/etc/rc4.d rw,relatime master:31 - ext4 /dev/sda3 rw,data=ordered
-1148 1119 1:1 /system-data/etc/rc5.d /var/lib/snapd/hostfs/etc/rc5.d rw,relatime master:32 - ext4 /dev/sda3 rw,data=ordered
-1149 1119 1:1 /system-data/etc/rc6.d /var/lib/snapd/hostfs/etc/rc6.d rw,relatime master:33 - ext4 /dev/sda3 rw,data=ordered
-1150 1119 1:1 /system-data/etc/rcS.d /var/lib/snapd/hostfs/etc/rcS.d rw,relatime master:34 - ext4 /dev/sda3 rw,data=ordered
-1151 1119 1:1 /system-data/etc/rsyslog.d /var/lib/snapd/hostfs/etc/rsyslog.d rw,relatime master:35 - ext4 /dev/sda3 rw,data=ordered
-1152 1119 1:1 /system-data/root/test-etc/shadow /var/lib/snapd/hostfs/etc/shadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1153 1119 1:1 /system-data/etc/ssh /var/lib/snapd/hostfs/etc/ssh rw,relatime master:36 - ext4 /dev/sda3 rw,data=ordered
-1154 1119 1:1 /system-data/etc/sudoers.d /var/lib/snapd/hostfs/etc/sudoers.d rw,relatime master:37 - ext4 /dev/sda3 rw,data=ordered
-1155 1119 1:1 /system-data/etc/sysctl.d /var/lib/snapd/hostfs/etc/sysctl.d rw,relatime master:38 - ext4 /dev/sda3 rw,data=ordered
-1156 1119 1:1 /system-data/etc/systemd/logind.conf.d /var/lib/snapd/hostfs/etc/systemd/logind.conf.d rw,relatime master:39 - ext4 /dev/sda3 rw,data=ordered
-1157 1119 1:1 /system-data/etc/systemd/network /var/lib/snapd/hostfs/etc/systemd/network rw,relatime master:40 - ext4 /dev/sda3 rw,data=ordered
-1158 1119 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:41 - ext4 /dev/sda3 rw,data=ordered
-1159 1158 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:42 - ext4 /dev/sda3 rw,data=ordered
-1160 1119 1:1 /system-data/etc/systemd/system.conf.d /var/lib/snapd/hostfs/etc/systemd/system.conf.d rw,relatime master:43 - ext4 /dev/sda3 rw,data=ordered
-1161 1119 1:1 /system-data/etc/systemd/timesyncd.conf /var/lib/snapd/hostfs/etc/systemd/timesyncd.conf rw,relatime master:44 - ext4 /dev/sda3 rw,data=ordered
-1162 1119 1:1 /system-data/etc/systemd/user.conf.d /var/lib/snapd/hostfs/etc/systemd/user.conf.d rw,relatime master:45 - ext4 /dev/sda3 rw,data=ordered
-1163 1119 1:1 /system-data/etc/udev/rules.d /var/lib/snapd/hostfs/etc/udev/rules.d rw,relatime master:46 - ext4 /dev/sda3 rw,data=ordered
-1164 1119 1:1 /system-data/etc/update-motd.d /var/lib/snapd/hostfs/etc/update-motd.d rw,relatime master:47 - ext4 /dev/sda3 rw,data=ordered
-1165 1119 1:1 /system-data/etc/writable /var/lib/snapd/hostfs/etc/writable rw,relatime master:48 - ext4 /dev/sda3 rw,data=ordered
-1166 1119 1:1 /user-data /var/lib/snapd/hostfs/home rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1167 1119 0:1 /firmware /var/lib/snapd/hostfs/lib/firmware ro,relatime master:49 - squashfs /dev/loop1 ro
-1168 1119 0:1 /modules /var/lib/snapd/hostfs/lib/modules ro,relatime master:50 - squashfs /dev/loop1 ro
-1169 1119 2:6 / /var/lib/snapd/hostfs/media rw,relatime master:51 - tmpfs tmpfs rw
-1170 1119 2:7 / /var/lib/snapd/hostfs/mnt rw,relatime master:52 - tmpfs tmpfs rw
-1171 1119 1:1 /system-data/root /var/lib/snapd/hostfs/root rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1172 1119 2:5 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:56 - tmpfs tmpfs rw,mode=755
-1173 1172 2:10 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:55 - tmpfs tmpfs rw,size=VARIABLE,mode=755
-1174 1173 2:11 / /var/lib/snapd/hostfs/run/cgmanager/fs rw,relatime master:57 - tmpfs cgmfs rw,size=VARIABLE,mode=755
-1175 1173 2:12 / /var/lib/snapd/hostfs/run/lock rw,nosuid,nodev,noexec,relatime master:58 - tmpfs tmpfs rw,size=VARIABLE
-1176 1173 2:10 /snapd/ns /var/lib/snapd/hostfs/run/snapd/ns rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=VARIABLE,mode=755
-1177 1173 2:13 / /var/lib/snapd/hostfs/run/user/0 rw,nosuid,nodev,relatime master:59 - tmpfs tmpfs rw,size=VARIABLE,mode=700
-1178 1119 1:1 /system-data/snap /var/lib/snapd/hostfs/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1179 1178 0:2 / /var/lib/snapd/hostfs/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-1180 1178 0:3 / /var/lib/snapd/hostfs/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-1181 1178 0:4 / /var/lib/snapd/hostfs/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-1182 1178 0:5 / /var/lib/snapd/hostfs/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-1183 1178 0:6 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-1184 1178 0:7 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-1185 1178 0:8 / /var/lib/snapd/hostfs/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-1186 1119 2:31 / /var/lib/snapd/hostfs/tmp rw,relatime master:84 - tmpfs tmpfs rw
-1187 1119 1:1 /system-data/var/cache/apparmor /var/lib/snapd/hostfs/var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1188 1119 1:1 /system-data/var/cache/snapd /var/lib/snapd/hostfs/var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1189 1119 1:1 /system-data/var/lib/apparmor /var/lib/snapd/hostfs/var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1190 1119 1:1 /system-data/var/lib/cloud /var/lib/snapd/hostfs/var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1191 1119 1:1 /system-data/var/lib/console-conf /var/lib/snapd/hostfs/var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1192 1119 1:1 /system-data/var/lib/dbus /var/lib/snapd/hostfs/var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1193 1119 1:1 /system-data/var/lib/dhcp /var/lib/snapd/hostfs/var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1194 1119 1:1 /system-data/var/lib/extrausers /var/lib/snapd/hostfs/var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1195 1119 1:1 /system-data/var/lib/initramfs-tools /var/lib/snapd/hostfs/var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1196 1119 1:1 /system-data/var/lib/logrotate /var/lib/snapd/hostfs/var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1197 1119 1:1 /system-data/var/lib/misc /var/lib/snapd/hostfs/var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1198 1119 1:1 /system-data/var/lib/snapd /var/lib/snapd/hostfs/var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1199 1119 2:32 / /var/lib/snapd/hostfs/var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
-1200 1119 1:1 /system-data/var/lib/systemd/random-seed /var/lib/snapd/hostfs/var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1201 1119 1:1 /system-data/var/lib/systemd/rfkill /var/lib/snapd/hostfs/var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1202 1119 1:1 /system-data/var/lib/waagent /var/lib/snapd/hostfs/var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1203 1119 1:1 /system-data/var/log /var/lib/snapd/hostfs/var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1204 1119 1:1 /system-data/var/snap /var/lib/snapd/hostfs/var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1205 1119 1:1 /system-data/var/tmp /var/lib/snapd/hostfs/var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1206 1119 1:1 / /var/lib/snapd/hostfs/writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1207 1206 0:2 / /var/lib/snapd/hostfs/writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-1208 1206 0:3 / /var/lib/snapd/hostfs/writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-1209 1206 0:4 / /var/lib/snapd/hostfs/writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-1210 1206 0:5 / /var/lib/snapd/hostfs/writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-1211 1206 0:6 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-1212 1206 0:7 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-1213 1206 0:8 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-1214 1001 2:32 / /var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
-1215 1001 1:1 /system-data/var/lib/systemd/random-seed /var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1216 1001 1:1 /system-data/var/lib/systemd/rfkill /var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1217 1001 1:1 /system-data/var/lib/waagent /var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1218 1001 1:1 /system-data/var/log /var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1219 1001 1:1 /system-data/var/snap /var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1220 1001 1:1 /system-data/var/tmp /var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1221 1001 1:1 / /writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-1222 1221 0:2 / /writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-1223 1221 0:3 / /writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-1224 1221 0:4 / /writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-1225 1221 0:5 / /writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-1226 1221 0:6 / /writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-1227 1221 0:7 / /writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-1228 1221 0:8 / /writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+1072 1071 0:2 / /snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+1073 1071 0:3 / /snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+1074 1071 0:4 / /snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+1075 1071 0:5 / /snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+1076 1071 0:6 / /snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+1077 1071 0:7 / /snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+1078 1071 0:8 / /snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+1079 1001 2:14 / /sys rw,nosuid,nodev,noexec,relatime master:67 - sysfs sysfs rw
+1080 1079 2:15 / /sys/fs/cgroup rw master:68 - tmpfs tmpfs rw,mode=755
+1081 1080 2:16 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime master:69 - cgroup cgroup rw,blkio
+1082 1080 2:17 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime master:70 - cgroup cgroup rw,cpu,cpuacct
+1083 1080 2:18 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime master:71 - cgroup cgroup rw,cpuset,clone_children
+1084 1080 2:19 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime master:72 - cgroup cgroup rw,devices
+1085 1080 2:20 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime master:73 - cgroup cgroup rw,freezer
+1086 1080 2:21 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime master:74 - cgroup cgroup rw,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb
+1087 1080 2:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime master:75 - cgroup cgroup rw,memory
+1088 1080 2:23 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime master:76 - cgroup cgroup rw,net_cls,net_prio
+1089 1080 2:24 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime master:77 - cgroup cgroup rw,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event
+1090 1080 2:25 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime master:78 - cgroup cgroup rw,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids
+1091 1080 2:26 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime master:79 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
+1092 1079 2:27 / /sys/fs/fuse/connections rw,relatime master:80 - fusectl fusectl rw
+1093 1079 2:28 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime master:81 - pstore pstore rw
+1094 1079 2:29 / /sys/kernel/debug rw,relatime master:82 - debugfs debugfs rw
+1095 1079 2:30 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime master:83 - securityfs securityfs rw
+1096 1001 2:31 / /tmp rw,relatime master:84 - tmpfs tmpfs rw
+1097 1096 2:31 /snap.test-snapd-mountinfo-core16/tmp /tmp rw,relatime - tmpfs tmpfs rw
+1098 1001 1:1 /system-data/var/cache/apparmor /var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1099 1001 1:1 /system-data/var/cache/snapd /var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1100 1001 1:1 /system-data/var/lib/apparmor /var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1101 1001 1:1 /system-data/var/lib/cloud /var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1102 1001 1:1 /system-data/var/lib/console-conf /var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1103 1001 1:1 /system-data/var/lib/dbus /var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1104 1001 1:1 /system-data/var/lib/dhcp /var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1105 1001 1:1 /system-data/var/lib/extrausers /var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1106 1001 1:1 /system-data/var/lib/initramfs-tools /var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1107 1001 1:1 /system-data/var/lib/logrotate /var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1108 1001 1:1 /system-data/var/lib/misc /var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1109 1001 1:1 /system-data/var/lib/snapd /var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1111 1110 0:0 / /var/lib/snapd/hostfs ro,relatime master:1 - squashfs /dev/loop0 ro
+1110 1109 1:1 /system-data/var/lib/snapd/hostfs /var/lib/snapd/hostfs rw,relatime - ext4 /dev/sda3 rw,data=ordered
+1112 1111 1:0 / /var/lib/snapd/hostfs/boot/efi rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
+1113 1111 1:0 /EFI/ubuntu /var/lib/snapd/hostfs/boot/grub rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
+1114 1111 1:1 /system-data/etc/apparmor.d/cache /var/lib/snapd/hostfs/etc/apparmor.d/cache rw,relatime master:8 - ext4 /dev/sda3 rw,data=ordered
+1115 1111 1:1 /system-data/etc/cloud /var/lib/snapd/hostfs/etc/cloud rw,relatime master:9 - ext4 /dev/sda3 rw,data=ordered
+1116 1111 1:1 /system-data/etc/dbus-1/system.d /var/lib/snapd/hostfs/etc/dbus-1/system.d rw,relatime master:10 - ext4 /dev/sda3 rw,data=ordered
+1117 1111 1:1 /system-data/etc/default/keyboard /var/lib/snapd/hostfs/etc/default/keyboard rw,relatime master:11 - ext4 /dev/sda3 rw,data=ordered
+1118 1111 1:1 /system-data/etc/default/swapfile /var/lib/snapd/hostfs/etc/default/swapfile rw,relatime master:12 - ext4 /dev/sda3 rw,data=ordered
+1119 1111 1:1 /system-data/etc/environment /var/lib/snapd/hostfs/etc/environment rw,relatime master:13 - ext4 /dev/sda3 rw,data=ordered
+1120 1111 2:5 /image.fstab /var/lib/snapd/hostfs/etc/fstab rw,nosuid,noexec,relatime master:14 - tmpfs tmpfs rw,mode=755
+1121 1111 1:1 /system-data/root/test-etc/group /var/lib/snapd/hostfs/etc/group ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1122 1111 1:1 /system-data/root/test-etc/gshadow /var/lib/snapd/hostfs/etc/gshadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1123 1111 1:1 /system-data/etc/hosts /var/lib/snapd/hostfs/etc/hosts rw,relatime master:16 - ext4 /dev/sda3 rw,data=ordered
+1124 1111 1:1 /system-data/etc/init /var/lib/snapd/hostfs/etc/init rw,relatime master:17 - ext4 /dev/sda3 rw,data=ordered
+1125 1111 1:1 /system-data/etc/init.d /var/lib/snapd/hostfs/etc/init.d rw,relatime master:18 - ext4 /dev/sda3 rw,data=ordered
+1126 1111 1:1 /system-data/etc/iproute2 /var/lib/snapd/hostfs/etc/iproute2 rw,relatime master:19 - ext4 /dev/sda3 rw,data=ordered
+1127 1111 1:1 /system-data/etc/machine-id /var/lib/snapd/hostfs/etc/machine-id rw,relatime master:20 - ext4 /dev/sda3 rw,data=ordered
+1128 1111 1:1 /system-data/etc/modprobe.d /var/lib/snapd/hostfs/etc/modprobe.d rw,relatime master:21 - ext4 /dev/sda3 rw,data=ordered
+1129 1111 1:1 /system-data/etc/modules-load.d /var/lib/snapd/hostfs/etc/modules-load.d rw,relatime master:22 - ext4 /dev/sda3 rw,data=ordered
+1130 1111 1:1 /system-data/etc/netplan /var/lib/snapd/hostfs/etc/netplan rw,relatime master:23 - ext4 /dev/sda3 rw,data=ordered
+1131 1111 1:1 /system-data/etc/network/if-up.d /var/lib/snapd/hostfs/etc/network/if-up.d rw,relatime master:24 - ext4 /dev/sda3 rw,data=ordered
+1132 1111 1:1 /system-data/etc/network/interfaces.d /var/lib/snapd/hostfs/etc/network/interfaces.d rw,relatime master:25 - ext4 /dev/sda3 rw,data=ordered
+1133 1111 1:1 /system-data/root/test-etc/passwd /var/lib/snapd/hostfs/etc/passwd ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1134 1111 1:1 /system-data/etc/ppp /var/lib/snapd/hostfs/etc/ppp rw,relatime master:26 - ext4 /dev/sda3 rw,data=ordered
+1135 1111 1:1 /system-data/etc/rc0.d /var/lib/snapd/hostfs/etc/rc0.d rw,relatime master:27 - ext4 /dev/sda3 rw,data=ordered
+1136 1111 1:1 /system-data/etc/rc1.d /var/lib/snapd/hostfs/etc/rc1.d rw,relatime master:28 - ext4 /dev/sda3 rw,data=ordered
+1137 1111 1:1 /system-data/etc/rc2.d /var/lib/snapd/hostfs/etc/rc2.d rw,relatime master:29 - ext4 /dev/sda3 rw,data=ordered
+1138 1111 1:1 /system-data/etc/rc3.d /var/lib/snapd/hostfs/etc/rc3.d rw,relatime master:30 - ext4 /dev/sda3 rw,data=ordered
+1139 1111 1:1 /system-data/etc/rc4.d /var/lib/snapd/hostfs/etc/rc4.d rw,relatime master:31 - ext4 /dev/sda3 rw,data=ordered
+1140 1111 1:1 /system-data/etc/rc5.d /var/lib/snapd/hostfs/etc/rc5.d rw,relatime master:32 - ext4 /dev/sda3 rw,data=ordered
+1141 1111 1:1 /system-data/etc/rc6.d /var/lib/snapd/hostfs/etc/rc6.d rw,relatime master:33 - ext4 /dev/sda3 rw,data=ordered
+1142 1111 1:1 /system-data/etc/rcS.d /var/lib/snapd/hostfs/etc/rcS.d rw,relatime master:34 - ext4 /dev/sda3 rw,data=ordered
+1143 1111 1:1 /system-data/etc/rsyslog.d /var/lib/snapd/hostfs/etc/rsyslog.d rw,relatime master:35 - ext4 /dev/sda3 rw,data=ordered
+1144 1111 1:1 /system-data/root/test-etc/shadow /var/lib/snapd/hostfs/etc/shadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1145 1111 1:1 /system-data/etc/ssh /var/lib/snapd/hostfs/etc/ssh rw,relatime master:36 - ext4 /dev/sda3 rw,data=ordered
+1146 1111 1:1 /system-data/etc/sudoers.d /var/lib/snapd/hostfs/etc/sudoers.d rw,relatime master:37 - ext4 /dev/sda3 rw,data=ordered
+1147 1111 1:1 /system-data/etc/sysctl.d /var/lib/snapd/hostfs/etc/sysctl.d rw,relatime master:38 - ext4 /dev/sda3 rw,data=ordered
+1148 1111 1:1 /system-data/etc/systemd/logind.conf.d /var/lib/snapd/hostfs/etc/systemd/logind.conf.d rw,relatime master:39 - ext4 /dev/sda3 rw,data=ordered
+1149 1111 1:1 /system-data/etc/systemd/network /var/lib/snapd/hostfs/etc/systemd/network rw,relatime master:40 - ext4 /dev/sda3 rw,data=ordered
+1150 1111 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:41 - ext4 /dev/sda3 rw,data=ordered
+1151 1150 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:42 - ext4 /dev/sda3 rw,data=ordered
+1152 1111 1:1 /system-data/etc/systemd/system.conf.d /var/lib/snapd/hostfs/etc/systemd/system.conf.d rw,relatime master:43 - ext4 /dev/sda3 rw,data=ordered
+1153 1111 1:1 /system-data/etc/systemd/timesyncd.conf /var/lib/snapd/hostfs/etc/systemd/timesyncd.conf rw,relatime master:44 - ext4 /dev/sda3 rw,data=ordered
+1154 1111 1:1 /system-data/etc/systemd/user.conf.d /var/lib/snapd/hostfs/etc/systemd/user.conf.d rw,relatime master:45 - ext4 /dev/sda3 rw,data=ordered
+1155 1111 1:1 /system-data/etc/udev/rules.d /var/lib/snapd/hostfs/etc/udev/rules.d rw,relatime master:46 - ext4 /dev/sda3 rw,data=ordered
+1156 1111 1:1 /system-data/etc/update-motd.d /var/lib/snapd/hostfs/etc/update-motd.d rw,relatime master:47 - ext4 /dev/sda3 rw,data=ordered
+1157 1111 1:1 /system-data/etc/writable /var/lib/snapd/hostfs/etc/writable rw,relatime master:48 - ext4 /dev/sda3 rw,data=ordered
+1158 1111 1:1 /user-data /var/lib/snapd/hostfs/home rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1159 1111 0:1 /firmware /var/lib/snapd/hostfs/lib/firmware ro,relatime master:49 - squashfs /dev/loop1 ro
+1160 1111 0:1 /modules /var/lib/snapd/hostfs/lib/modules ro,relatime master:50 - squashfs /dev/loop1 ro
+1161 1111 2:6 / /var/lib/snapd/hostfs/media rw,relatime master:51 - tmpfs tmpfs rw
+1162 1111 2:7 / /var/lib/snapd/hostfs/mnt rw,relatime master:52 - tmpfs tmpfs rw
+1163 1111 1:1 /system-data/root /var/lib/snapd/hostfs/root rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1164 1111 2:5 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:56 - tmpfs tmpfs rw,mode=755
+1165 1164 2:10 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:55 - tmpfs tmpfs rw,size=VARIABLE,mode=755
+1166 1165 2:11 / /var/lib/snapd/hostfs/run/cgmanager/fs rw,relatime master:57 - tmpfs cgmfs rw,size=VARIABLE,mode=755
+1167 1165 2:12 / /var/lib/snapd/hostfs/run/lock rw,nosuid,nodev,noexec,relatime master:58 - tmpfs tmpfs rw,size=VARIABLE
+1168 1165 2:10 /snapd/ns /var/lib/snapd/hostfs/run/snapd/ns rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=VARIABLE,mode=755
+1169 1165 2:13 / /var/lib/snapd/hostfs/run/user/0 rw,nosuid,nodev,relatime master:59 - tmpfs tmpfs rw,size=VARIABLE,mode=700
+1170 1111 1:1 /system-data/snap /var/lib/snapd/hostfs/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1171 1170 0:2 / /var/lib/snapd/hostfs/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+1172 1170 0:3 / /var/lib/snapd/hostfs/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+1173 1170 0:4 / /var/lib/snapd/hostfs/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+1174 1170 0:5 / /var/lib/snapd/hostfs/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+1175 1170 0:6 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+1176 1170 0:7 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+1177 1170 0:8 / /var/lib/snapd/hostfs/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+1178 1111 2:31 / /var/lib/snapd/hostfs/tmp rw,relatime master:84 - tmpfs tmpfs rw
+1179 1111 1:1 /system-data/var/cache/apparmor /var/lib/snapd/hostfs/var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1180 1111 1:1 /system-data/var/cache/snapd /var/lib/snapd/hostfs/var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1181 1111 1:1 /system-data/var/lib/apparmor /var/lib/snapd/hostfs/var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1182 1111 1:1 /system-data/var/lib/cloud /var/lib/snapd/hostfs/var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1183 1111 1:1 /system-data/var/lib/console-conf /var/lib/snapd/hostfs/var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1184 1111 1:1 /system-data/var/lib/dbus /var/lib/snapd/hostfs/var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1185 1111 1:1 /system-data/var/lib/dhcp /var/lib/snapd/hostfs/var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1186 1111 1:1 /system-data/var/lib/extrausers /var/lib/snapd/hostfs/var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1187 1111 1:1 /system-data/var/lib/initramfs-tools /var/lib/snapd/hostfs/var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1188 1111 1:1 /system-data/var/lib/logrotate /var/lib/snapd/hostfs/var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1189 1111 1:1 /system-data/var/lib/misc /var/lib/snapd/hostfs/var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1190 1111 1:1 /system-data/var/lib/snapd /var/lib/snapd/hostfs/var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1191 1111 2:32 / /var/lib/snapd/hostfs/var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
+1192 1111 1:1 /system-data/var/lib/systemd/random-seed /var/lib/snapd/hostfs/var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1193 1111 1:1 /system-data/var/lib/systemd/rfkill /var/lib/snapd/hostfs/var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1194 1111 1:1 /system-data/var/lib/waagent /var/lib/snapd/hostfs/var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1195 1111 1:1 /system-data/var/log /var/lib/snapd/hostfs/var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1196 1111 1:1 /system-data/var/snap /var/lib/snapd/hostfs/var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1197 1111 1:1 /system-data/var/tmp /var/lib/snapd/hostfs/var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1198 1111 1:1 / /var/lib/snapd/hostfs/writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1199 1198 0:2 / /var/lib/snapd/hostfs/writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+1200 1198 0:3 / /var/lib/snapd/hostfs/writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+1201 1198 0:4 / /var/lib/snapd/hostfs/writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+1202 1198 0:5 / /var/lib/snapd/hostfs/writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+1203 1198 0:6 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+1204 1198 0:7 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+1205 1198 0:8 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+1206 1001 2:32 / /var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
+1207 1001 1:1 /system-data/var/lib/systemd/random-seed /var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1208 1001 1:1 /system-data/var/lib/systemd/rfkill /var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1209 1001 1:1 /system-data/var/lib/waagent /var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1210 1001 1:1 /system-data/var/log /var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1211 1001 1:1 /system-data/var/snap /var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1212 1001 1:1 /system-data/var/tmp /var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1213 1001 1:1 / /writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+1214 1213 0:2 / /writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+1215 1213 0:3 / /writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+1216 1213 0:4 / /writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+1217 1213 0:5 / /writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+1218 1213 0:6 / /writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+1219 1213 0:7 / /writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+1220 1213 0:8 / /writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
diff --git a/tests/main/mount-ns/google.ubuntu-core-16-64/PER-USER-16.expected.txt b/tests/main/mount-ns/google.ubuntu-core-16-64/PER-USER-16.expected.txt
index 4cef5723c5..76ce4d303f 100644
--- a/tests/main/mount-ns/google.ubuntu-core-16-64/PER-USER-16.expected.txt
+++ b/tests/main/mount-ns/google.ubuntu-core-16-64/PER-USER-16.expected.txt
@@ -69,160 +69,152 @@
2069 2065 2:10 /snapd/ns /run/snapd/ns rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=VARIABLE,mode=755
2070 2065 2:13 / /run/user/0 rw,nosuid,nodev,relatime master:59 - tmpfs tmpfs rw,size=VARIABLE,mode=700
2071 2001 1:1 /system-data/snap /snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2072 2071 1:1 /system-data/snap /snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2073 2071 0:2 / /snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-2074 2072 0:2 / /snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-2075 2071 0:3 / /snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-2076 2072 0:3 / /snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-2077 2071 0:4 / /snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-2078 2072 0:4 / /snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-2079 2071 0:5 / /snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-2080 2072 0:5 / /snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-2081 2071 0:6 / /snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-2082 2072 0:6 / /snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-2083 2071 0:7 / /snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-2084 2072 0:7 / /snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-2085 2071 0:8 / /snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-2086 2072 0:8 / /snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-2087 2001 2:14 / /sys rw,nosuid,nodev,noexec,relatime master:67 - sysfs sysfs rw
-2088 2087 2:15 / /sys/fs/cgroup rw master:68 - tmpfs tmpfs rw,mode=755
-2089 2088 2:16 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime master:69 - cgroup cgroup rw,blkio
-2090 2088 2:17 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime master:70 - cgroup cgroup rw,cpu,cpuacct
-2091 2088 2:18 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime master:71 - cgroup cgroup rw,cpuset,clone_children
-2092 2088 2:19 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime master:72 - cgroup cgroup rw,devices
-2093 2088 2:20 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime master:73 - cgroup cgroup rw,freezer
-2094 2088 2:21 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime master:74 - cgroup cgroup rw,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb
-2095 2088 2:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime master:75 - cgroup cgroup rw,memory
-2096 2088 2:23 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime master:76 - cgroup cgroup rw,net_cls,net_prio
-2097 2088 2:24 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime master:77 - cgroup cgroup rw,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event
-2098 2088 2:25 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime master:78 - cgroup cgroup rw,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids
-2099 2088 2:26 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime master:79 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
-2100 2087 2:27 / /sys/fs/fuse/connections rw,relatime master:80 - fusectl fusectl rw
-2101 2087 2:28 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime master:81 - pstore pstore rw
-2102 2087 2:29 / /sys/kernel/debug rw,relatime master:82 - debugfs debugfs rw
-2103 2087 2:30 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime master:83 - securityfs securityfs rw
-2104 2001 2:31 / /tmp rw,relatime master:84 - tmpfs tmpfs rw
-2105 2104 2:31 /snap.test-snapd-mountinfo-core16/tmp /tmp rw,relatime - tmpfs tmpfs rw
-2106 2001 1:1 /system-data/var/cache/apparmor /var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2107 2001 1:1 /system-data/var/cache/snapd /var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2108 2001 1:1 /system-data/var/lib/apparmor /var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2109 2001 1:1 /system-data/var/lib/cloud /var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2110 2001 1:1 /system-data/var/lib/console-conf /var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2111 2001 1:1 /system-data/var/lib/dbus /var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2112 2001 1:1 /system-data/var/lib/dhcp /var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2113 2001 1:1 /system-data/var/lib/extrausers /var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2114 2001 1:1 /system-data/var/lib/initramfs-tools /var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2115 2001 1:1 /system-data/var/lib/logrotate /var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2116 2001 1:1 /system-data/var/lib/misc /var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2117 2001 1:1 /system-data/var/lib/snapd /var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2119 2118 0:0 / /var/lib/snapd/hostfs ro,relatime master:1 - squashfs /dev/loop0 ro
-2118 2117 1:1 /system-data/var/lib/snapd/hostfs /var/lib/snapd/hostfs rw,relatime - ext4 /dev/sda3 rw,data=ordered
-2120 2119 1:0 / /var/lib/snapd/hostfs/boot/efi rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
-2121 2119 1:0 /EFI/ubuntu /var/lib/snapd/hostfs/boot/grub rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
-2122 2119 1:1 /system-data/etc/apparmor.d/cache /var/lib/snapd/hostfs/etc/apparmor.d/cache rw,relatime master:8 - ext4 /dev/sda3 rw,data=ordered
-2123 2119 1:1 /system-data/etc/cloud /var/lib/snapd/hostfs/etc/cloud rw,relatime master:9 - ext4 /dev/sda3 rw,data=ordered
-2124 2119 1:1 /system-data/etc/dbus-1/system.d /var/lib/snapd/hostfs/etc/dbus-1/system.d rw,relatime master:10 - ext4 /dev/sda3 rw,data=ordered
-2125 2119 1:1 /system-data/etc/default/keyboard /var/lib/snapd/hostfs/etc/default/keyboard rw,relatime master:11 - ext4 /dev/sda3 rw,data=ordered
-2126 2119 1:1 /system-data/etc/default/swapfile /var/lib/snapd/hostfs/etc/default/swapfile rw,relatime master:12 - ext4 /dev/sda3 rw,data=ordered
-2127 2119 1:1 /system-data/etc/environment /var/lib/snapd/hostfs/etc/environment rw,relatime master:13 - ext4 /dev/sda3 rw,data=ordered
-2128 2119 2:5 /image.fstab /var/lib/snapd/hostfs/etc/fstab rw,nosuid,noexec,relatime master:14 - tmpfs tmpfs rw,mode=755
-2129 2119 1:1 /system-data/root/test-etc/group /var/lib/snapd/hostfs/etc/group ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2130 2119 1:1 /system-data/root/test-etc/gshadow /var/lib/snapd/hostfs/etc/gshadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2131 2119 1:1 /system-data/etc/hosts /var/lib/snapd/hostfs/etc/hosts rw,relatime master:16 - ext4 /dev/sda3 rw,data=ordered
-2132 2119 1:1 /system-data/etc/init /var/lib/snapd/hostfs/etc/init rw,relatime master:17 - ext4 /dev/sda3 rw,data=ordered
-2133 2119 1:1 /system-data/etc/init.d /var/lib/snapd/hostfs/etc/init.d rw,relatime master:18 - ext4 /dev/sda3 rw,data=ordered
-2134 2119 1:1 /system-data/etc/iproute2 /var/lib/snapd/hostfs/etc/iproute2 rw,relatime master:19 - ext4 /dev/sda3 rw,data=ordered
-2135 2119 1:1 /system-data/etc/machine-id /var/lib/snapd/hostfs/etc/machine-id rw,relatime master:20 - ext4 /dev/sda3 rw,data=ordered
-2136 2119 1:1 /system-data/etc/modprobe.d /var/lib/snapd/hostfs/etc/modprobe.d rw,relatime master:21 - ext4 /dev/sda3 rw,data=ordered
-2137 2119 1:1 /system-data/etc/modules-load.d /var/lib/snapd/hostfs/etc/modules-load.d rw,relatime master:22 - ext4 /dev/sda3 rw,data=ordered
-2138 2119 1:1 /system-data/etc/netplan /var/lib/snapd/hostfs/etc/netplan rw,relatime master:23 - ext4 /dev/sda3 rw,data=ordered
-2139 2119 1:1 /system-data/etc/network/if-up.d /var/lib/snapd/hostfs/etc/network/if-up.d rw,relatime master:24 - ext4 /dev/sda3 rw,data=ordered
-2140 2119 1:1 /system-data/etc/network/interfaces.d /var/lib/snapd/hostfs/etc/network/interfaces.d rw,relatime master:25 - ext4 /dev/sda3 rw,data=ordered
-2141 2119 1:1 /system-data/root/test-etc/passwd /var/lib/snapd/hostfs/etc/passwd ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2142 2119 1:1 /system-data/etc/ppp /var/lib/snapd/hostfs/etc/ppp rw,relatime master:26 - ext4 /dev/sda3 rw,data=ordered
-2143 2119 1:1 /system-data/etc/rc0.d /var/lib/snapd/hostfs/etc/rc0.d rw,relatime master:27 - ext4 /dev/sda3 rw,data=ordered
-2144 2119 1:1 /system-data/etc/rc1.d /var/lib/snapd/hostfs/etc/rc1.d rw,relatime master:28 - ext4 /dev/sda3 rw,data=ordered
-2145 2119 1:1 /system-data/etc/rc2.d /var/lib/snapd/hostfs/etc/rc2.d rw,relatime master:29 - ext4 /dev/sda3 rw,data=ordered
-2146 2119 1:1 /system-data/etc/rc3.d /var/lib/snapd/hostfs/etc/rc3.d rw,relatime master:30 - ext4 /dev/sda3 rw,data=ordered
-2147 2119 1:1 /system-data/etc/rc4.d /var/lib/snapd/hostfs/etc/rc4.d rw,relatime master:31 - ext4 /dev/sda3 rw,data=ordered
-2148 2119 1:1 /system-data/etc/rc5.d /var/lib/snapd/hostfs/etc/rc5.d rw,relatime master:32 - ext4 /dev/sda3 rw,data=ordered
-2149 2119 1:1 /system-data/etc/rc6.d /var/lib/snapd/hostfs/etc/rc6.d rw,relatime master:33 - ext4 /dev/sda3 rw,data=ordered
-2150 2119 1:1 /system-data/etc/rcS.d /var/lib/snapd/hostfs/etc/rcS.d rw,relatime master:34 - ext4 /dev/sda3 rw,data=ordered
-2151 2119 1:1 /system-data/etc/rsyslog.d /var/lib/snapd/hostfs/etc/rsyslog.d rw,relatime master:35 - ext4 /dev/sda3 rw,data=ordered
-2152 2119 1:1 /system-data/root/test-etc/shadow /var/lib/snapd/hostfs/etc/shadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2153 2119 1:1 /system-data/etc/ssh /var/lib/snapd/hostfs/etc/ssh rw,relatime master:36 - ext4 /dev/sda3 rw,data=ordered
-2154 2119 1:1 /system-data/etc/sudoers.d /var/lib/snapd/hostfs/etc/sudoers.d rw,relatime master:37 - ext4 /dev/sda3 rw,data=ordered
-2155 2119 1:1 /system-data/etc/sysctl.d /var/lib/snapd/hostfs/etc/sysctl.d rw,relatime master:38 - ext4 /dev/sda3 rw,data=ordered
-2156 2119 1:1 /system-data/etc/systemd/logind.conf.d /var/lib/snapd/hostfs/etc/systemd/logind.conf.d rw,relatime master:39 - ext4 /dev/sda3 rw,data=ordered
-2157 2119 1:1 /system-data/etc/systemd/network /var/lib/snapd/hostfs/etc/systemd/network rw,relatime master:40 - ext4 /dev/sda3 rw,data=ordered
-2158 2119 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:41 - ext4 /dev/sda3 rw,data=ordered
-2159 2158 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:42 - ext4 /dev/sda3 rw,data=ordered
-2160 2119 1:1 /system-data/etc/systemd/system.conf.d /var/lib/snapd/hostfs/etc/systemd/system.conf.d rw,relatime master:43 - ext4 /dev/sda3 rw,data=ordered
-2161 2119 1:1 /system-data/etc/systemd/timesyncd.conf /var/lib/snapd/hostfs/etc/systemd/timesyncd.conf rw,relatime master:44 - ext4 /dev/sda3 rw,data=ordered
-2162 2119 1:1 /system-data/etc/systemd/user.conf.d /var/lib/snapd/hostfs/etc/systemd/user.conf.d rw,relatime master:45 - ext4 /dev/sda3 rw,data=ordered
-2163 2119 1:1 /system-data/etc/udev/rules.d /var/lib/snapd/hostfs/etc/udev/rules.d rw,relatime master:46 - ext4 /dev/sda3 rw,data=ordered
-2164 2119 1:1 /system-data/etc/update-motd.d /var/lib/snapd/hostfs/etc/update-motd.d rw,relatime master:47 - ext4 /dev/sda3 rw,data=ordered
-2165 2119 1:1 /system-data/etc/writable /var/lib/snapd/hostfs/etc/writable rw,relatime master:48 - ext4 /dev/sda3 rw,data=ordered
-2166 2119 1:1 /user-data /var/lib/snapd/hostfs/home rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2167 2119 0:1 /firmware /var/lib/snapd/hostfs/lib/firmware ro,relatime master:49 - squashfs /dev/loop1 ro
-2168 2119 0:1 /modules /var/lib/snapd/hostfs/lib/modules ro,relatime master:50 - squashfs /dev/loop1 ro
-2169 2119 2:6 / /var/lib/snapd/hostfs/media rw,relatime master:51 - tmpfs tmpfs rw
-2170 2119 2:7 / /var/lib/snapd/hostfs/mnt rw,relatime master:52 - tmpfs tmpfs rw
-2171 2119 1:1 /system-data/root /var/lib/snapd/hostfs/root rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2172 2119 2:5 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:56 - tmpfs tmpfs rw,mode=755
-2173 2172 2:10 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:55 - tmpfs tmpfs rw,size=VARIABLE,mode=755
-2174 2173 2:11 / /var/lib/snapd/hostfs/run/cgmanager/fs rw,relatime master:57 - tmpfs cgmfs rw,size=VARIABLE,mode=755
-2175 2173 2:12 / /var/lib/snapd/hostfs/run/lock rw,nosuid,nodev,noexec,relatime master:58 - tmpfs tmpfs rw,size=VARIABLE
-2176 2173 2:10 /snapd/ns /var/lib/snapd/hostfs/run/snapd/ns rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=VARIABLE,mode=755
-2177 2173 2:13 / /var/lib/snapd/hostfs/run/user/0 rw,nosuid,nodev,relatime master:59 - tmpfs tmpfs rw,size=VARIABLE,mode=700
-2178 2119 1:1 /system-data/snap /var/lib/snapd/hostfs/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2179 2178 0:2 / /var/lib/snapd/hostfs/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-2180 2178 0:3 / /var/lib/snapd/hostfs/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-2181 2178 0:4 / /var/lib/snapd/hostfs/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-2182 2178 0:5 / /var/lib/snapd/hostfs/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-2183 2178 0:6 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-2184 2178 0:7 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-2185 2178 0:8 / /var/lib/snapd/hostfs/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-2186 2119 2:31 / /var/lib/snapd/hostfs/tmp rw,relatime master:84 - tmpfs tmpfs rw
-2187 2119 1:1 /system-data/var/cache/apparmor /var/lib/snapd/hostfs/var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2188 2119 1:1 /system-data/var/cache/snapd /var/lib/snapd/hostfs/var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2189 2119 1:1 /system-data/var/lib/apparmor /var/lib/snapd/hostfs/var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2190 2119 1:1 /system-data/var/lib/cloud /var/lib/snapd/hostfs/var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2191 2119 1:1 /system-data/var/lib/console-conf /var/lib/snapd/hostfs/var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2192 2119 1:1 /system-data/var/lib/dbus /var/lib/snapd/hostfs/var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2193 2119 1:1 /system-data/var/lib/dhcp /var/lib/snapd/hostfs/var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2194 2119 1:1 /system-data/var/lib/extrausers /var/lib/snapd/hostfs/var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2195 2119 1:1 /system-data/var/lib/initramfs-tools /var/lib/snapd/hostfs/var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2196 2119 1:1 /system-data/var/lib/logrotate /var/lib/snapd/hostfs/var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2197 2119 1:1 /system-data/var/lib/misc /var/lib/snapd/hostfs/var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2198 2119 1:1 /system-data/var/lib/snapd /var/lib/snapd/hostfs/var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2199 2119 2:32 / /var/lib/snapd/hostfs/var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
-2200 2119 1:1 /system-data/var/lib/systemd/random-seed /var/lib/snapd/hostfs/var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2201 2119 1:1 /system-data/var/lib/systemd/rfkill /var/lib/snapd/hostfs/var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2202 2119 1:1 /system-data/var/lib/waagent /var/lib/snapd/hostfs/var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2203 2119 1:1 /system-data/var/log /var/lib/snapd/hostfs/var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2204 2119 1:1 /system-data/var/snap /var/lib/snapd/hostfs/var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2205 2119 1:1 /system-data/var/tmp /var/lib/snapd/hostfs/var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2206 2119 1:1 / /var/lib/snapd/hostfs/writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2207 2206 0:2 / /var/lib/snapd/hostfs/writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-2208 2206 0:3 / /var/lib/snapd/hostfs/writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-2209 2206 0:4 / /var/lib/snapd/hostfs/writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-2210 2206 0:5 / /var/lib/snapd/hostfs/writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-2211 2206 0:6 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-2212 2206 0:7 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-2213 2206 0:8 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
-2214 2001 2:32 / /var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
-2215 2001 1:1 /system-data/var/lib/systemd/random-seed /var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2216 2001 1:1 /system-data/var/lib/systemd/rfkill /var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2217 2001 1:1 /system-data/var/lib/waagent /var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2218 2001 1:1 /system-data/var/log /var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2219 2001 1:1 /system-data/var/snap /var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2220 2001 1:1 /system-data/var/tmp /var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2221 2001 1:1 / /writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
-2222 2221 0:2 / /writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
-2223 2221 0:3 / /writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
-2224 2221 0:4 / /writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
-2225 2221 0:5 / /writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
-2226 2221 0:6 / /writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
-2227 2221 0:7 / /writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
-2228 2221 0:8 / /writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+2072 2071 0:2 / /snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+2073 2071 0:3 / /snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+2074 2071 0:4 / /snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+2075 2071 0:5 / /snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+2076 2071 0:6 / /snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+2077 2071 0:7 / /snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+2078 2071 0:8 / /snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+2079 2001 2:14 / /sys rw,nosuid,nodev,noexec,relatime master:67 - sysfs sysfs rw
+2080 2079 2:15 / /sys/fs/cgroup rw master:68 - tmpfs tmpfs rw,mode=755
+2081 2080 2:16 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime master:69 - cgroup cgroup rw,blkio
+2082 2080 2:17 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime master:70 - cgroup cgroup rw,cpu,cpuacct
+2083 2080 2:18 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime master:71 - cgroup cgroup rw,cpuset,clone_children
+2084 2080 2:19 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime master:72 - cgroup cgroup rw,devices
+2085 2080 2:20 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime master:73 - cgroup cgroup rw,freezer
+2086 2080 2:21 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime master:74 - cgroup cgroup rw,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb
+2087 2080 2:22 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime master:75 - cgroup cgroup rw,memory
+2088 2080 2:23 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime master:76 - cgroup cgroup rw,net_cls,net_prio
+2089 2080 2:24 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime master:77 - cgroup cgroup rw,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event
+2090 2080 2:25 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime master:78 - cgroup cgroup rw,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids
+2091 2080 2:26 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime master:79 - cgroup cgroup rw,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd
+2092 2079 2:27 / /sys/fs/fuse/connections rw,relatime master:80 - fusectl fusectl rw
+2093 2079 2:28 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime master:81 - pstore pstore rw
+2094 2079 2:29 / /sys/kernel/debug rw,relatime master:82 - debugfs debugfs rw
+2095 2079 2:30 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime master:83 - securityfs securityfs rw
+2096 2001 2:31 / /tmp rw,relatime master:84 - tmpfs tmpfs rw
+2097 2096 2:31 /snap.test-snapd-mountinfo-core16/tmp /tmp rw,relatime - tmpfs tmpfs rw
+2098 2001 1:1 /system-data/var/cache/apparmor /var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2099 2001 1:1 /system-data/var/cache/snapd /var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2100 2001 1:1 /system-data/var/lib/apparmor /var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2101 2001 1:1 /system-data/var/lib/cloud /var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2102 2001 1:1 /system-data/var/lib/console-conf /var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2103 2001 1:1 /system-data/var/lib/dbus /var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2104 2001 1:1 /system-data/var/lib/dhcp /var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2105 2001 1:1 /system-data/var/lib/extrausers /var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2106 2001 1:1 /system-data/var/lib/initramfs-tools /var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2107 2001 1:1 /system-data/var/lib/logrotate /var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2108 2001 1:1 /system-data/var/lib/misc /var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2109 2001 1:1 /system-data/var/lib/snapd /var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2111 2110 0:0 / /var/lib/snapd/hostfs ro,relatime master:1 - squashfs /dev/loop0 ro
+2110 2109 1:1 /system-data/var/lib/snapd/hostfs /var/lib/snapd/hostfs rw,relatime - ext4 /dev/sda3 rw,data=ordered
+2112 2111 1:0 / /var/lib/snapd/hostfs/boot/efi rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
+2113 2111 1:0 /EFI/ubuntu /var/lib/snapd/hostfs/boot/grub rw,relatime master:2 - vfat /dev/sda2 rw,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro
+2114 2111 1:1 /system-data/etc/apparmor.d/cache /var/lib/snapd/hostfs/etc/apparmor.d/cache rw,relatime master:8 - ext4 /dev/sda3 rw,data=ordered
+2115 2111 1:1 /system-data/etc/cloud /var/lib/snapd/hostfs/etc/cloud rw,relatime master:9 - ext4 /dev/sda3 rw,data=ordered
+2116 2111 1:1 /system-data/etc/dbus-1/system.d /var/lib/snapd/hostfs/etc/dbus-1/system.d rw,relatime master:10 - ext4 /dev/sda3 rw,data=ordered
+2117 2111 1:1 /system-data/etc/default/keyboard /var/lib/snapd/hostfs/etc/default/keyboard rw,relatime master:11 - ext4 /dev/sda3 rw,data=ordered
+2118 2111 1:1 /system-data/etc/default/swapfile /var/lib/snapd/hostfs/etc/default/swapfile rw,relatime master:12 - ext4 /dev/sda3 rw,data=ordered
+2119 2111 1:1 /system-data/etc/environment /var/lib/snapd/hostfs/etc/environment rw,relatime master:13 - ext4 /dev/sda3 rw,data=ordered
+2120 2111 2:5 /image.fstab /var/lib/snapd/hostfs/etc/fstab rw,nosuid,noexec,relatime master:14 - tmpfs tmpfs rw,mode=755
+2121 2111 1:1 /system-data/root/test-etc/group /var/lib/snapd/hostfs/etc/group ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2122 2111 1:1 /system-data/root/test-etc/gshadow /var/lib/snapd/hostfs/etc/gshadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2123 2111 1:1 /system-data/etc/hosts /var/lib/snapd/hostfs/etc/hosts rw,relatime master:16 - ext4 /dev/sda3 rw,data=ordered
+2124 2111 1:1 /system-data/etc/init /var/lib/snapd/hostfs/etc/init rw,relatime master:17 - ext4 /dev/sda3 rw,data=ordered
+2125 2111 1:1 /system-data/etc/init.d /var/lib/snapd/hostfs/etc/init.d rw,relatime master:18 - ext4 /dev/sda3 rw,data=ordered
+2126 2111 1:1 /system-data/etc/iproute2 /var/lib/snapd/hostfs/etc/iproute2 rw,relatime master:19 - ext4 /dev/sda3 rw,data=ordered
+2127 2111 1:1 /system-data/etc/machine-id /var/lib/snapd/hostfs/etc/machine-id rw,relatime master:20 - ext4 /dev/sda3 rw,data=ordered
+2128 2111 1:1 /system-data/etc/modprobe.d /var/lib/snapd/hostfs/etc/modprobe.d rw,relatime master:21 - ext4 /dev/sda3 rw,data=ordered
+2129 2111 1:1 /system-data/etc/modules-load.d /var/lib/snapd/hostfs/etc/modules-load.d rw,relatime master:22 - ext4 /dev/sda3 rw,data=ordered
+2130 2111 1:1 /system-data/etc/netplan /var/lib/snapd/hostfs/etc/netplan rw,relatime master:23 - ext4 /dev/sda3 rw,data=ordered
+2131 2111 1:1 /system-data/etc/network/if-up.d /var/lib/snapd/hostfs/etc/network/if-up.d rw,relatime master:24 - ext4 /dev/sda3 rw,data=ordered
+2132 2111 1:1 /system-data/etc/network/interfaces.d /var/lib/snapd/hostfs/etc/network/interfaces.d rw,relatime master:25 - ext4 /dev/sda3 rw,data=ordered
+2133 2111 1:1 /system-data/root/test-etc/passwd /var/lib/snapd/hostfs/etc/passwd ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2134 2111 1:1 /system-data/etc/ppp /var/lib/snapd/hostfs/etc/ppp rw,relatime master:26 - ext4 /dev/sda3 rw,data=ordered
+2135 2111 1:1 /system-data/etc/rc0.d /var/lib/snapd/hostfs/etc/rc0.d rw,relatime master:27 - ext4 /dev/sda3 rw,data=ordered
+2136 2111 1:1 /system-data/etc/rc1.d /var/lib/snapd/hostfs/etc/rc1.d rw,relatime master:28 - ext4 /dev/sda3 rw,data=ordered
+2137 2111 1:1 /system-data/etc/rc2.d /var/lib/snapd/hostfs/etc/rc2.d rw,relatime master:29 - ext4 /dev/sda3 rw,data=ordered
+2138 2111 1:1 /system-data/etc/rc3.d /var/lib/snapd/hostfs/etc/rc3.d rw,relatime master:30 - ext4 /dev/sda3 rw,data=ordered
+2139 2111 1:1 /system-data/etc/rc4.d /var/lib/snapd/hostfs/etc/rc4.d rw,relatime master:31 - ext4 /dev/sda3 rw,data=ordered
+2140 2111 1:1 /system-data/etc/rc5.d /var/lib/snapd/hostfs/etc/rc5.d rw,relatime master:32 - ext4 /dev/sda3 rw,data=ordered
+2141 2111 1:1 /system-data/etc/rc6.d /var/lib/snapd/hostfs/etc/rc6.d rw,relatime master:33 - ext4 /dev/sda3 rw,data=ordered
+2142 2111 1:1 /system-data/etc/rcS.d /var/lib/snapd/hostfs/etc/rcS.d rw,relatime master:34 - ext4 /dev/sda3 rw,data=ordered
+2143 2111 1:1 /system-data/etc/rsyslog.d /var/lib/snapd/hostfs/etc/rsyslog.d rw,relatime master:35 - ext4 /dev/sda3 rw,data=ordered
+2144 2111 1:1 /system-data/root/test-etc/shadow /var/lib/snapd/hostfs/etc/shadow ro,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2145 2111 1:1 /system-data/etc/ssh /var/lib/snapd/hostfs/etc/ssh rw,relatime master:36 - ext4 /dev/sda3 rw,data=ordered
+2146 2111 1:1 /system-data/etc/sudoers.d /var/lib/snapd/hostfs/etc/sudoers.d rw,relatime master:37 - ext4 /dev/sda3 rw,data=ordered
+2147 2111 1:1 /system-data/etc/sysctl.d /var/lib/snapd/hostfs/etc/sysctl.d rw,relatime master:38 - ext4 /dev/sda3 rw,data=ordered
+2148 2111 1:1 /system-data/etc/systemd/logind.conf.d /var/lib/snapd/hostfs/etc/systemd/logind.conf.d rw,relatime master:39 - ext4 /dev/sda3 rw,data=ordered
+2149 2111 1:1 /system-data/etc/systemd/network /var/lib/snapd/hostfs/etc/systemd/network rw,relatime master:40 - ext4 /dev/sda3 rw,data=ordered
+2150 2111 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:41 - ext4 /dev/sda3 rw,data=ordered
+2151 2150 1:1 /system-data/etc/systemd/system /var/lib/snapd/hostfs/etc/systemd/system rw,relatime master:42 - ext4 /dev/sda3 rw,data=ordered
+2152 2111 1:1 /system-data/etc/systemd/system.conf.d /var/lib/snapd/hostfs/etc/systemd/system.conf.d rw,relatime master:43 - ext4 /dev/sda3 rw,data=ordered
+2153 2111 1:1 /system-data/etc/systemd/timesyncd.conf /var/lib/snapd/hostfs/etc/systemd/timesyncd.conf rw,relatime master:44 - ext4 /dev/sda3 rw,data=ordered
+2154 2111 1:1 /system-data/etc/systemd/user.conf.d /var/lib/snapd/hostfs/etc/systemd/user.conf.d rw,relatime master:45 - ext4 /dev/sda3 rw,data=ordered
+2155 2111 1:1 /system-data/etc/udev/rules.d /var/lib/snapd/hostfs/etc/udev/rules.d rw,relatime master:46 - ext4 /dev/sda3 rw,data=ordered
+2156 2111 1:1 /system-data/etc/update-motd.d /var/lib/snapd/hostfs/etc/update-motd.d rw,relatime master:47 - ext4 /dev/sda3 rw,data=ordered
+2157 2111 1:1 /system-data/etc/writable /var/lib/snapd/hostfs/etc/writable rw,relatime master:48 - ext4 /dev/sda3 rw,data=ordered
+2158 2111 1:1 /user-data /var/lib/snapd/hostfs/home rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2159 2111 0:1 /firmware /var/lib/snapd/hostfs/lib/firmware ro,relatime master:49 - squashfs /dev/loop1 ro
+2160 2111 0:1 /modules /var/lib/snapd/hostfs/lib/modules ro,relatime master:50 - squashfs /dev/loop1 ro
+2161 2111 2:6 / /var/lib/snapd/hostfs/media rw,relatime master:51 - tmpfs tmpfs rw
+2162 2111 2:7 / /var/lib/snapd/hostfs/mnt rw,relatime master:52 - tmpfs tmpfs rw
+2163 2111 1:1 /system-data/root /var/lib/snapd/hostfs/root rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2164 2111 2:5 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:56 - tmpfs tmpfs rw,mode=755
+2165 2164 2:10 / /var/lib/snapd/hostfs/run rw,nosuid,noexec,relatime master:55 - tmpfs tmpfs rw,size=VARIABLE,mode=755
+2166 2165 2:11 / /var/lib/snapd/hostfs/run/cgmanager/fs rw,relatime master:57 - tmpfs cgmfs rw,size=VARIABLE,mode=755
+2167 2165 2:12 / /var/lib/snapd/hostfs/run/lock rw,nosuid,nodev,noexec,relatime master:58 - tmpfs tmpfs rw,size=VARIABLE
+2168 2165 2:10 /snapd/ns /var/lib/snapd/hostfs/run/snapd/ns rw,nosuid,noexec,relatime - tmpfs tmpfs rw,size=VARIABLE,mode=755
+2169 2165 2:13 / /var/lib/snapd/hostfs/run/user/0 rw,nosuid,nodev,relatime master:59 - tmpfs tmpfs rw,size=VARIABLE,mode=700
+2170 2111 1:1 /system-data/snap /var/lib/snapd/hostfs/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2171 2170 0:2 / /var/lib/snapd/hostfs/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+2172 2170 0:3 / /var/lib/snapd/hostfs/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+2173 2170 0:4 / /var/lib/snapd/hostfs/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+2174 2170 0:5 / /var/lib/snapd/hostfs/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+2175 2170 0:6 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+2176 2170 0:7 / /var/lib/snapd/hostfs/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+2177 2170 0:8 / /var/lib/snapd/hostfs/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+2178 2111 2:31 / /var/lib/snapd/hostfs/tmp rw,relatime master:84 - tmpfs tmpfs rw
+2179 2111 1:1 /system-data/var/cache/apparmor /var/lib/snapd/hostfs/var/cache/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2180 2111 1:1 /system-data/var/cache/snapd /var/lib/snapd/hostfs/var/cache/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2181 2111 1:1 /system-data/var/lib/apparmor /var/lib/snapd/hostfs/var/lib/apparmor rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2182 2111 1:1 /system-data/var/lib/cloud /var/lib/snapd/hostfs/var/lib/cloud rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2183 2111 1:1 /system-data/var/lib/console-conf /var/lib/snapd/hostfs/var/lib/console-conf rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2184 2111 1:1 /system-data/var/lib/dbus /var/lib/snapd/hostfs/var/lib/dbus rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2185 2111 1:1 /system-data/var/lib/dhcp /var/lib/snapd/hostfs/var/lib/dhcp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2186 2111 1:1 /system-data/var/lib/extrausers /var/lib/snapd/hostfs/var/lib/extrausers rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2187 2111 1:1 /system-data/var/lib/initramfs-tools /var/lib/snapd/hostfs/var/lib/initramfs-tools rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2188 2111 1:1 /system-data/var/lib/logrotate /var/lib/snapd/hostfs/var/lib/logrotate rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2189 2111 1:1 /system-data/var/lib/misc /var/lib/snapd/hostfs/var/lib/misc rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2190 2111 1:1 /system-data/var/lib/snapd /var/lib/snapd/hostfs/var/lib/snapd rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2191 2111 2:32 / /var/lib/snapd/hostfs/var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
+2192 2111 1:1 /system-data/var/lib/systemd/random-seed /var/lib/snapd/hostfs/var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2193 2111 1:1 /system-data/var/lib/systemd/rfkill /var/lib/snapd/hostfs/var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2194 2111 1:1 /system-data/var/lib/waagent /var/lib/snapd/hostfs/var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2195 2111 1:1 /system-data/var/log /var/lib/snapd/hostfs/var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2196 2111 1:1 /system-data/var/snap /var/lib/snapd/hostfs/var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2197 2111 1:1 /system-data/var/tmp /var/lib/snapd/hostfs/var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2198 2111 1:1 / /var/lib/snapd/hostfs/writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2199 2198 0:2 / /var/lib/snapd/hostfs/writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+2200 2198 0:3 / /var/lib/snapd/hostfs/writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+2201 2198 0:4 / /var/lib/snapd/hostfs/writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+2202 2198 0:5 / /var/lib/snapd/hostfs/writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+2203 2198 0:6 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+2204 2198 0:7 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+2205 2198 0:8 / /var/lib/snapd/hostfs/writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
+2206 2001 2:32 / /var/lib/sudo rw,relatime master:85 - tmpfs tmpfs rw,mode=700
+2207 2001 1:1 /system-data/var/lib/systemd/random-seed /var/lib/systemd/random-seed rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2208 2001 1:1 /system-data/var/lib/systemd/rfkill /var/lib/systemd/rfkill rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2209 2001 1:1 /system-data/var/lib/waagent /var/lib/waagent rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2210 2001 1:1 /system-data/var/log /var/log rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2211 2001 1:1 /system-data/var/snap /var/snap rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2212 2001 1:1 /system-data/var/tmp /var/tmp rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2213 2001 1:1 / /writable rw,relatime master:15 - ext4 /dev/sda3 rw,data=ordered
+2214 2213 0:2 / /writable/system-data/snap/core/1 ro,nodev,relatime master:60 - squashfs /dev/loop2 ro
+2215 2213 0:3 / /writable/system-data/snap/core18/1 ro,nodev,relatime master:61 - squashfs /dev/loop3 ro
+2216 2213 0:4 / /writable/system-data/snap/pc-kernel/1 ro,nodev,relatime master:62 - squashfs /dev/loop4 ro
+2217 2213 0:5 / /writable/system-data/snap/pc/1 ro,nodev,relatime master:63 - squashfs /dev/loop5 ro
+2218 2213 0:6 / /writable/system-data/snap/test-snapd-mountinfo-core16/1 ro,nodev,relatime master:64 - squashfs /dev/loop6 ro
+2219 2213 0:7 / /writable/system-data/snap/test-snapd-mountinfo-core18/1 ro,nodev,relatime master:65 - squashfs /dev/loop7 ro
+2220 2213 0:8 / /writable/system-data/snap/test-snapd-rsync/1 ro,nodev,relatime master:66 - squashfs /dev/loop8 ro
diff --git a/tests/main/ubuntu-core-upgrade/task.yaml b/tests/main/ubuntu-core-upgrade/task.yaml
index 4e80af8c89..af675d06f6 100644
--- a/tests/main/ubuntu-core-upgrade/task.yaml
+++ b/tests/main/ubuntu-core-upgrade/task.yaml
@@ -1,6 +1,6 @@
summary: Upgrade the core snap and revert a few times
-systems: [ubuntu-core-16-*]
+systems: [ubuntu-core-1*]
# Start early as it takes a long time.
priority: 100
@@ -24,15 +24,30 @@ restore: |
snap remove core --revision=x3
prepare: |
- snap list | awk "/^core / {print(\$3)}" > nextBoot
+ #shellcheck source=tests/lib/systems.sh
+ . "$TESTSLIB"/systems.sh
+
+ TARGET_SNAP=core
+ if is_core18_system; then
+ TARGET_SNAP=core18
+ fi
+
+ snap list | awk "/^${TARGET_SNAP} / {print(\$3)}" > nextBoot
snap install test-snapd-tools
execute: |
#shellcheck source=tests/lib/boot.sh
. "$TESTSLIB"/boot.sh
+ #shellcheck source=tests/lib/systems.sh
+ . "$TESTSLIB"/systems.sh
+
+ TARGET_SNAP=core
+ if is_core18_system; then
+ TARGET_SNAP=core18
+ fi
# FIXME Why it starting with snap_mode=try the first time?
- # Perhaps because core is installed after seeding? Do we
+ # Perhaps because $TARGET_SNAP is installed after seeding? Do we
# want that on pristine images?
if [ "$SPREAD_REBOOT" != 0 ]; then
echo "Waiting for snapd to clean snap_mode"
@@ -41,12 +56,12 @@ execute: |
done
echo "Ensure the bootloader is correct after reboot"
- test "$(bootenv snap_core)" = "core_$(cat nextBoot).snap"
+ test "$(bootenv snap_core)" = "${TARGET_SNAP}_$(cat nextBoot).snap"
test "$(bootenv snap_try_core)" = ""
test "$(bootenv snap_mode)" = ""
fi
- snap list | awk "/^core / {print(\$3)}" > prevBoot
+ snap list | awk "/^${TARGET_SNAP} / {print(\$3)}" > prevBoot
# wait for ongoing change if there is one
if [ -f curChg ] ; then
@@ -56,10 +71,10 @@ execute: |
case "$SPREAD_REBOOT" in
- 0) cmd="snap install --dangerous /var/lib/snapd/snaps/core_$(cat prevBoot).snap" ;;
- 1) cmd="snap revert core" ;;
- 2) cmd="snap install --dangerous /var/lib/snapd/snaps/core_$(cat prevBoot).snap" ;;
- 3) cmd="snap revert core" ;;
+ 0) cmd="snap install --dangerous /var/lib/snapd/snaps/${TARGET_SNAP}_$(cat prevBoot).snap" ;;
+ 1) cmd="snap revert $TARGET_SNAP" ;;
+ 2) cmd="snap install --dangerous /var/lib/snapd/snaps/${TARGET_SNAP}_$(cat prevBoot).snap" ;;
+ 3) cmd="snap revert $TARGET_SNAP" ;;
4) exit 0 ;;
esac
@@ -78,9 +93,9 @@ execute: |
test-snapd-tools.echo hello | MATCH hello
echo "Ensure the bootloader is correct before reboot"
- readlink /snap/core/current > nextBoot
+ readlink "/snap/${TARGET_SNAP}/current" > nextBoot
test "$(cat prevBoot)" != "$(cat nextBoot)"
- test "$(bootenv snap_try_core)" = "core_$(cat nextBoot).snap"
+ test "$(bootenv snap_try_core)" = "${TARGET_SNAP}_$(cat nextBoot).snap"
test "$(bootenv snap_mode)" = "try"
echo "Ensure the device is scheduled for auto-reboot"
diff --git a/tests/main/ubuntu-core-writablepaths/task.yaml b/tests/main/ubuntu-core-writablepaths/task.yaml
index 0c6d1e8e9a..16bf5c4b10 100644
--- a/tests/main/ubuntu-core-writablepaths/task.yaml
+++ b/tests/main/ubuntu-core-writablepaths/task.yaml
@@ -1,6 +1,6 @@
summary: Ensure that the writable paths on the image are correct
-systems: [ubuntu-core-16-*]
+systems: [ubuntu-core-1*]
execute: |
echo "Ensure everything in writable-paths is actually writable"
diff --git a/tests/regression/lp-1667385/task.yaml b/tests/regression/lp-1667385/task.yaml
index 716c221ad4..e6c56755c5 100644
--- a/tests/regression/lp-1667385/task.yaml
+++ b/tests/regression/lp-1667385/task.yaml
@@ -5,7 +5,7 @@ details: |
(e.g. from when the user installed it) should be preserved.
# run on ubuntu-{14,16,18,20+} and ubuntu-core-16-*
-systems: [ubuntu-1*, ubuntu-2*, ubuntu-3*, ubuntu-core-16-*]
+systems: [ubuntu-1*, ubuntu-2*, ubuntu-3*, ubuntu-core-1*]
environment:
FLAG/jailmode: jailmode
diff --git a/tests/regression/lp-1802581/task.yaml b/tests/regression/lp-1802581/task.yaml
index eb10e281ea..38e51a13e3 100644
--- a/tests/regression/lp-1802581/task.yaml
+++ b/tests/regression/lp-1802581/task.yaml
@@ -17,7 +17,7 @@ description: |
exporting the pin, would always run before the apparmor backend, which
could now rely on the pin being exposed to userspace.
-systems: [ubuntu-core-16-64]
+systems: [ubuntu-core-1*-64]
prepare: |
# Core image that were created using spread will have a fake "gpio-pin".