diff options
51 files changed, 893 insertions, 443 deletions
diff --git a/arch/arch.go b/arch/arch.go index d98139476e..9b8a578f36 100644 --- a/arch/arch.go +++ b/arch/arch.go @@ -33,33 +33,30 @@ type ArchitectureType string // change the architecture. This is important to e.g. install // armhf snaps onto a armhf image that is generated on an amd64 // machine -var arch = ArchitectureType(ubuntuArchFromGoArch(runtime.GOARCH)) +var arch = ArchitectureType(dpkgArchFromGoArch(runtime.GOARCH)) // SetArchitecture allows overriding the auto detected Architecture func SetArchitecture(newArch ArchitectureType) { arch = newArch } -// FIXME: rename all Ubuntu*Architecture() to SnapdArchitecture() -// (or DpkgArchitecture) - -// UbuntuArchitecture returns the debian equivalent architecture for the +// DpkgArchitecture returns the debian equivalent architecture for the // currently running architecture. // // If the architecture does not map any debian architecture, the // GOARCH is returned. -func UbuntuArchitecture() string { +func DpkgArchitecture() string { return string(arch) } -// ubuntuArchFromGoArch maps a go architecture string to the coresponding -// Ubuntu architecture string. +// dpkgArchFromGoArch maps a go architecture string to the coresponding +// Debian equivalent architecture string. // // E.g. the go "386" architecture string maps to the ubuntu "i386" // architecture. -func ubuntuArchFromGoArch(goarch string) string { +func dpkgArchFromGoArch(goarch string) string { goArchMapping := map[string]string{ - // go ubuntu + // go dpkg "386": "i386", "amd64": "amd64", "arm": "armhf", @@ -82,27 +79,27 @@ func ubuntuArchFromGoArch(goarch string) string { } } - ubuntuArch := goArchMapping[goarch] - if ubuntuArch == "" { + dpkgArch := goArchMapping[goarch] + if dpkgArch == "" { log.Panicf("unknown goarch %q", goarch) } - return ubuntuArch + return dpkgArch } -// UbuntuKernelArchitecture return the debian equivalent architecture +// DpkgKernelArchitecture returns the debian equivalent architecture // for the current running kernel. This is usually the same as the -// UbuntuArchitecture - however there maybe cases that you run e.g. +// DpkgArchitecture - however there maybe cases that you run e.g. // a snapd:i386 on an amd64 kernel. -func UbuntuKernelArchitecture() string { - return ubuntuArchFromKernelArch(osutil.MachineName()) +func DpkgKernelArchitecture() string { + return dpkgArchFromKernelArch(osutil.MachineName()) } -// ubuntuArchFromkernelArch maps the kernel architecture as reported +// dpkgArchFromkernelArch maps the kernel architecture as reported // via uname() to the dpkg architecture -func ubuntuArchFromKernelArch(utsMachine string) string { +func dpkgArchFromKernelArch(utsMachine string) string { kernelArchMapping := map[string]string{ - // kernel ubuntu + // kernel dpkg "i686": "i386", "x86_64": "amd64", "armv7l": "armhf", @@ -115,12 +112,12 @@ func ubuntuArchFromKernelArch(utsMachine string) string { "ppc64": "ppc64", } - ubuntuArch := kernelArchMapping[utsMachine] - if ubuntuArch == "" { + dpkgArch := kernelArchMapping[utsMachine] + if dpkgArch == "" { log.Panicf("unknown kernel arch %q", utsMachine) } - return ubuntuArch + return dpkgArch } // IsSupportedArchitecture returns true if the system architecture is in the diff --git a/arch/arch_test.go b/arch/arch_test.go index cc5c82c4a9..ef3e6401af 100644 --- a/arch/arch_test.go +++ b/arch/arch_test.go @@ -33,24 +33,24 @@ var _ = Suite(&ArchTestSuite{}) type ArchTestSuite struct { } -func (ts *ArchTestSuite) TestUbuntuArchitecture(c *C) { - c.Check(ubuntuArchFromGoArch("386"), Equals, "i386") - c.Check(ubuntuArchFromGoArch("amd64"), Equals, "amd64") - c.Check(ubuntuArchFromGoArch("arm"), Equals, "armhf") - c.Check(ubuntuArchFromGoArch("arm64"), Equals, "arm64") - c.Check(ubuntuArchFromGoArch("ppc64le"), Equals, "ppc64el") - c.Check(ubuntuArchFromGoArch("ppc64"), Equals, "ppc64") - c.Check(ubuntuArchFromGoArch("s390x"), Equals, "s390x") - c.Check(ubuntuArchFromGoArch("ppc"), Equals, "powerpc") - c.Check(ubuntuArchFromGoArch("ppc64"), Equals, "ppc64") +func (ts *ArchTestSuite) TestArchDpkgArchitecture(c *C) { + c.Check(dpkgArchFromGoArch("386"), Equals, "i386") + c.Check(dpkgArchFromGoArch("amd64"), Equals, "amd64") + c.Check(dpkgArchFromGoArch("arm"), Equals, "armhf") + c.Check(dpkgArchFromGoArch("arm64"), Equals, "arm64") + c.Check(dpkgArchFromGoArch("ppc64le"), Equals, "ppc64el") + c.Check(dpkgArchFromGoArch("ppc64"), Equals, "ppc64") + c.Check(dpkgArchFromGoArch("s390x"), Equals, "s390x") + c.Check(dpkgArchFromGoArch("ppc"), Equals, "powerpc") + c.Check(dpkgArchFromGoArch("ppc64"), Equals, "ppc64") } -func (ts *ArchTestSuite) TestSetArchitecture(c *C) { +func (ts *ArchTestSuite) TestArchSetArchitecture(c *C) { SetArchitecture("armhf") - c.Assert(UbuntuArchitecture(), Equals, "armhf") + c.Assert(DpkgArchitecture(), Equals, "armhf") } -func (ts *ArchTestSuite) TestSupportedArchitectures(c *C) { +func (ts *ArchTestSuite) TestArchSupportedArchitectures(c *C) { arch = "armhf" c.Check(IsSupportedArchitecture([]string{"all"}), Equals, true) c.Check(IsSupportedArchitecture([]string{"amd64", "armhf", "powerpc"}), Equals, true) diff --git a/asserts/model.go b/asserts/model.go index e7b5150ed3..4cc8beeb6d 100644 --- a/asserts/model.go +++ b/asserts/model.go @@ -95,7 +95,7 @@ var ( defaultModes = []string{"run"} ) -func checkExtendSnaps(extendedSnaps interface{}, base string) (*modelSnaps, error) { +func checkExtendedSnaps(extendedSnaps interface{}, base string) (*modelSnaps, error) { const wrongHeaderType = `"snaps" header must be a list of maps` entries, ok := extendedSnaps.([]interface{}) @@ -234,7 +234,7 @@ func checkModelSnap(snap map[string]interface{}) (*ModelSnap, error) { } } else { trackCh, err := channel.ParseVerbatim(track, "-") - if err != nil || trackCh.Track == "" || trackCh.Risk != "" || trackCh.Branch != "" { + if err != nil || !trackCh.VerbatimTrackOnly() { return nil, fmt.Errorf("invalid locked track for snap %q: %s", name, track) } } @@ -604,7 +604,7 @@ func assembleModel(assert assertionBase) (Assertion, error) { var modSnaps *modelSnaps if extended { // TODO: support and consider grade! - modSnaps, err = checkExtendSnaps(extendedSnaps, base) + modSnaps, err = checkExtendedSnaps(extendedSnaps, base) if err != nil { return nil, err } diff --git a/boot/boot.go b/boot/boot.go index 19b1b23a5f..217c50b7bf 100644 --- a/boot/boot.go +++ b/boot/boot.go @@ -22,10 +22,13 @@ package boot import ( "errors" "fmt" + "os" "path/filepath" "strings" + "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/bootloader" + "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/logger" "github.com/snapcore/snapd/snap" ) @@ -273,3 +276,92 @@ func MarkBootSuccessful() error { return bl.SetBootVars(m) } + +// MakeBootable sets up the image filesystem with the given rootdir +// such that it can be booted. bootWith is a map from the paths in the +// image seed for kernel and boot base to their snap info. This +// entails: +// - installing the bootloader configuration from the gadget +// - creating symlinks for boot snaps from seed to the runtime blob dir +// - setting boot env vars pointing to the revisions of the boot snaps to use +// - extracting kernel assets as needed by the bootloader +func MakeBootable(model *asserts.Model, rootdir string, bootWith map[string]*snap.Info, unpackedGadgetDir string) error { + if len(bootWith) != 2 { + return fmt.Errorf("internal error: MakeBootable can only be called with exactly one kernel and exactly one core/base boot info: %v", bootWith) + } + + // install the bootloader configuration from the gadget + if err := bootloader.InstallBootConfig(unpackedGadgetDir, rootdir); err != nil { + return err + } + + // setup symlinks for kernel and boot base from the blob directory + // to the seed snaps + + snapBlobDir := dirs.SnapBlobDirUnder(rootdir) + if err := os.MkdirAll(snapBlobDir, 0755); err != nil { + return err + } + + for fn := range bootWith { + dst := filepath.Join(snapBlobDir, filepath.Base(fn)) + // construct a relative symlink from the blob dir + // to the seed snap file + relSymlink, err := filepath.Rel(snapBlobDir, fn) + if err != nil { + return fmt.Errorf("cannot build symlink for boot snap: %v", err) + } + if err := os.Symlink(relSymlink, dst); err != nil { + return err + } + } + + // 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. + bl, err := bootloader.Find(rootdir, &bootloader.Options{ + PrepareImageTime: true, + }) + if err != nil { + return fmt.Errorf("cannot set kernel/core boot variables: %s", err) + } + + m := map[string]string{ + "snap_mode": "", + "snap_try_core": "", + "snap_try_kernel": "", + } + if model.DisplayName() != "" { + m["snap_menuentry"] = model.DisplayName() + } + + for fn, info := range bootWith { + bootvar := "" + + switch info.GetType() { + case snap.TypeOS, snap.TypeBase: + bootvar = "snap_core" + case snap.TypeKernel: + snapf, err := snap.Open(fn) + if err != nil { + return err + } + + if err := bl.ExtractKernelAssets(info, snapf); err != nil { + return err + } + bootvar = "snap_kernel" + } + + if bootvar != "" { + name := filepath.Base(fn) + m[bootvar] = name + } + } + if err := bl.SetBootVars(m); err != nil { + return err + } + + return nil + +} diff --git a/boot/boot_test.go b/boot/boot_test.go index 0e02caaae4..d963d76cbf 100644 --- a/boot/boot_test.go +++ b/boot/boot_test.go @@ -21,17 +21,22 @@ package boot_test import ( "errors" + "io/ioutil" + "os" "path/filepath" "testing" . "gopkg.in/check.v1" + "github.com/snapcore/snapd/asserts" + "github.com/snapcore/snapd/asserts/assertstest" "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/snap" + "github.com/snapcore/snapd/snap/snaptest" "github.com/snapcore/snapd/testutil" ) @@ -359,3 +364,93 @@ func (s *bootSetSuite) TestMarkBootSuccessfulKKernelUpdate(c *C) { "snap_kernel": "k2", }) } + +func (s *bootSetSuite) makeSnap(c *C, name, yaml string, revno snap.Revision) (fn string, info *snap.Info) { + si := &snap.SideInfo{ + RealName: name, + Revision: revno, + } + fn = snaptest.MakeTestSnapWithFiles(c, yaml, nil) + snapf, err := snap.Open(fn) + c.Assert(err, IsNil) + info, err = snap.ReadInfoFromSnapFile(snapf, si) + c.Assert(err, IsNil) + return fn, info +} + +func (s *bootSetSuite) TestMakeBootable(c *C) { + dirs.SetRootDir("") + + headers := map[string]interface{}{ + "type": "model", + "authority-id": "my-brand", + "series": "16", + "brand-id": "my-brand", + "model": "my-model", + "display-name": "My Model", + "architecture": "amd64", + "base": "core18", + "gadget": "pc=18", + "kernel": "pc-kernel=18", + "timestamp": "2018-01-01T08:00:00+00:00", + } + model := assertstest.FakeAssertion(headers).(*asserts.Model) + + grubCfg := []byte("#grub cfg") + unpackedGadgetDir := c.MkDir() + err := ioutil.WriteFile(filepath.Join(unpackedGadgetDir, "grub.conf"), grubCfg, 0644) + c.Assert(err, IsNil) + + rootdir := c.MkDir() + + seedSnapsDirs := filepath.Join(rootdir, "/var/lib/snapd/seed", "snaps") + err = os.MkdirAll(seedSnapsDirs, 0755) + c.Assert(err, IsNil) + + baseFn, baseInfo := s.makeSnap(c, "core18", `name: core18 +type: base +version: 4.0 +`, snap.R(3)) + baseInSeed := filepath.Join(seedSnapsDirs, filepath.Base(baseInfo.MountFile())) + err = os.Rename(baseFn, baseInSeed) + c.Assert(err, IsNil) + kernelFn, kernelInfo := s.makeSnap(c, "pc-kernel", `name: pc-kernel +type: kernel +version: 4.0 +`, snap.R(5)) + kernelInSeed := filepath.Join(seedSnapsDirs, filepath.Base(kernelInfo.MountFile())) + err = os.Rename(kernelFn, kernelInSeed) + c.Assert(err, IsNil) + + err = boot.MakeBootable(model, rootdir, map[string]*snap.Info{ + kernelInSeed: kernelInfo, + baseInSeed: baseInfo, + }, unpackedGadgetDir) + c.Assert(err, IsNil) + + // check the bootloader config + m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core", "snap_menuentry") + c.Assert(err, IsNil) + c.Check(m["snap_kernel"], Equals, "pc-kernel_5.snap") + c.Check(m["snap_core"], Equals, "core18_3.snap") + c.Check(m["snap_menuentry"], Equals, "My Model") + + // kernel was extracted as needed + c.Check(s.bootloader.ExtractKernelAssetsCalls, DeepEquals, []snap.PlaceInfo{kernelInfo}) + + // check symlinks from snap blob dir + kernelBlob := filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(kernelInfo.MountFile())) + dst, err := os.Readlink(filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(kernelInfo.MountFile()))) + c.Assert(err, IsNil) + c.Check(dst, Equals, "../seed/snaps/pc-kernel_5.snap") + c.Check(kernelBlob, testutil.FilePresent) + + baseBlob := filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(baseInfo.MountFile())) + dst, err = os.Readlink(filepath.Join(dirs.SnapBlobDirUnder(rootdir), filepath.Base(baseInfo.MountFile()))) + c.Assert(err, IsNil) + c.Check(dst, Equals, "../seed/snaps/core18_3.snap") + c.Check(baseBlob, testutil.FilePresent) + + // check that the bootloader (grub here) configuration was copied + c.Check(filepath.Join(rootdir, "boot", "grub/grub.cfg"), testutil.FileEquals, grubCfg) +} diff --git a/cmd/Makefile.am b/cmd/Makefile.am index e10dee262d..804f9f78e6 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -69,6 +69,7 @@ new_format = \ snap-confine/seccomp-support-ext.h \ snap-confine/selinux-support.c \ snap-confine/selinux-support.h \ + snap-confine/snap-confine-invocation-test.c \ snap-confine/snap-confine-invocation.c \ snap-confine/snap-confine-invocation.h \ snap-discard-ns/snap-discard-ns.c @@ -336,8 +337,7 @@ snap_confine_unit_tests_SOURCES = \ snap-confine/mount-support-test.c \ snap-confine/ns-support-test.c \ snap-confine/snap-confine-args-test.c \ - snap-confine/snap-confine-invocation.c \ - snap-confine/snap-confine-invocation.h \ + snap-confine/snap-confine-invocation-test.c \ snap-confine/snap-device-helper-test.c snap_confine_unit_tests_CFLAGS = $(snap_confine_snap_confine_CFLAGS) $(GLIB_CFLAGS) snap_confine_unit_tests_LDADD = $(snap_confine_snap_confine_LDADD) $(GLIB_LIBS) diff --git a/cmd/libsnap-confine-private/snap-test.c b/cmd/libsnap-confine-private/snap-test.c index 1141531319..9499703cdd 100644 --- a/cmd/libsnap-confine-private/snap-test.c +++ b/cmd/libsnap-confine-private/snap-test.c @@ -405,7 +405,9 @@ static void test_sc_snap_drop_instance_key_short_dest_max(void) if (g_test_subprocess()) { char dest[SNAP_NAME_LEN + 1] = { 0 }; /* 40 chars (max valid length), pretend dest is the same length, no space for terminator */ - sc_snap_drop_instance_key("01234567890123456789012345678901234567890", dest, sizeof dest - 1); + sc_snap_drop_instance_key + ("01234567890123456789012345678901234567890", dest, + sizeof dest - 1); return; } g_test_trap_subprocess(NULL, 0, 0); @@ -437,7 +439,8 @@ static void test_sc_snap_drop_instance_key_basic(void) memset(name, 0xff, sizeof name); /* 40 chars - snap name length */ - sc_snap_drop_instance_key("0123456789012345678901234567890123456789", name, sizeof name); + sc_snap_drop_instance_key("0123456789012345678901234567890123456789", + name, sizeof name); g_assert_cmpstr(name, ==, "0123456789012345678901234567890123456789"); } @@ -570,7 +573,7 @@ static void __attribute__((constructor)) init(void) g_test_add_func("/snap/sc_snap_drop_instance_key/short_dest2", test_sc_snap_drop_instance_key_short_dest2); g_test_add_func("/snap/sc_snap_drop_instance_key/short_dest_max", - test_sc_snap_drop_instance_key_short_dest_max); + test_sc_snap_drop_instance_key_short_dest_max); g_test_add_func("/snap/sc_snap_split_instance_name/basic", test_sc_snap_split_instance_name_basic); diff --git a/cmd/libsnap-confine-private/test-utils-test.c b/cmd/libsnap-confine-private/test-utils-test.c index cd760a1e8b..6d0a2d0cac 100644 --- a/cmd/libsnap-confine-private/test-utils-test.c +++ b/cmd/libsnap-confine-private/test-utils-test.c @@ -39,7 +39,31 @@ static void test_rm_rf_tmp(void) g_test_trap_assert_failed(); } +static void test_test_argc_argv(void) +{ + // Check that test_argc_argv() correctly stores data + int argc = 0; + char **argv = NULL; + + test_argc_argv(&argc, &argv, NULL); + g_assert_cmpint(argc, ==, 0); + g_assert_nonnull(argv); + g_assert_null(argv[0]); + + argc = 0; + argv = NULL; + + test_argc_argv(&argc, &argv, "zero", "one", "two", NULL); + g_assert_cmpint(argc, ==, 3); + g_assert_nonnull(argv); + g_assert_cmpstr(argv[0], ==, "zero"); + g_assert_cmpstr(argv[1], ==, "one"); + g_assert_cmpstr(argv[2], ==, "two"); + g_assert_null(argv[3]); +} + static void __attribute__((constructor)) init(void) { g_test_add_func("/test-utils/rm_rf_tmp", test_rm_rf_tmp); + g_test_add_func("/test-utils/test_argc_argv", test_test_argc_argv); } diff --git a/cmd/libsnap-confine-private/test-utils.c b/cmd/libsnap-confine-private/test-utils.c index ab13ce4b7c..c3e17a18d9 100644 --- a/cmd/libsnap-confine-private/test-utils.c +++ b/cmd/libsnap-confine-private/test-utils.c @@ -16,6 +16,7 @@ */ #include "test-utils.h" +#include "string-utils.h" #include "error.h" #include "utils.h" @@ -71,3 +72,37 @@ void rm_rf_tmp(const char *dir) g_free(argv[3]); g_free(argv); } + +void + __attribute__((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...) +{ + int argc = 0; + char **argv = NULL; + va_list ap; + + /* find out how many elements there are */ + va_start(ap, argvp); + while (NULL != va_arg(ap, const char *)) { + argc += 1; + } + va_end(ap); + + /* argc + terminating NULL entry */ + argv = calloc(argc + 1, sizeof argv[0]); + g_assert_nonnull(argv); + + va_start(ap, argvp); + for (int i = 0; i < argc; i++) { + const char *arg = va_arg(ap, const char *); + char *arg_copy = sc_strdup(arg); + g_test_queue_free(arg_copy); + argv[i] = arg_copy; + } + va_end(ap); + + /* free argv last, so that entries do not leak */ + g_test_queue_free(argv); + + *argcp = argc; + *argvp = argv; +} diff --git a/cmd/libsnap-confine-private/test-utils.h b/cmd/libsnap-confine-private/test-utils.h index e13ffcbfd7..2b41ed93a3 100644 --- a/cmd/libsnap-confine-private/test-utils.h +++ b/cmd/libsnap-confine-private/test-utils.h @@ -23,4 +23,10 @@ */ void rm_rf_tmp(const char *dir); +/** + * Create an argc + argv pair out of a NULL terminated argument list. + **/ +void + __attribute__((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...); + #endif diff --git a/cmd/snap-confine/cookie-support-test.c b/cmd/snap-confine/cookie-support-test.c index 754ed7e092..f8ba544099 100644 --- a/cmd/snap-confine/cookie-support-test.c +++ b/cmd/snap-confine/cookie-support-test.c @@ -18,6 +18,7 @@ #include "cookie-support.h" #include "cookie-support.c" +#include "../libsnap-confine-private/cleanup-funcs.h" #include "../libsnap-confine-private/test-utils.h" #include <glib.h> @@ -64,8 +65,8 @@ static void create_dumy_cookie_file(const char *snap_name, static void test_cookie_get_from_snapd__successful(void) { - struct sc_error *err = NULL; - char *cookie; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + char *cookie SC_CLEANUP(sc_cleanup_string) = NULL; char *dummy = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijmnopqrst"; @@ -81,8 +82,8 @@ static void test_cookie_get_from_snapd__successful(void) static void test_cookie_get_from_snapd__nofile(void) { - struct sc_error *err = NULL; - char *cookie; + struct sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + char *cookie SC_CLEANUP(sc_cleanup_string) = NULL; set_fake_cookie_dir(); diff --git a/cmd/snap-confine/mount-support.c b/cmd/snap-confine/mount-support.c index 411fbd4598..4bdf6feee5 100644 --- a/cmd/snap-confine/mount-support.c +++ b/cmd/snap-confine/mount-support.c @@ -241,10 +241,10 @@ static void sc_bootstrap_mount_namespace(const struct sc_mount_config *config) // replicate the state of the root filesystem into the scratch directory. sc_do_mount(config->rootfs_dir, scratch_dir, NULL, MS_REC | MS_BIND, NULL); - // Make the scratch directory recursively private. Nothing done there will - // be shared with any peer group, This effectively detaches us from the - // original namespace and coupled with pivot_root below serves as the - // foundation of the mount sandbox. + // Make the scratch directory recursively slave. Nothing done there will be + // shared with the initial mount namespace. This effectively detaches us, + // in one way, from the original namespace and coupled with pivot_root + // below serves as the foundation of the mount sandbox. sc_do_mount("none", scratch_dir, NULL, MS_REC | MS_SLAVE, NULL); // Bind mount certain directories from the host filesystem to the scratch // directory. By default mount events will propagate in both into and out diff --git a/cmd/snap-confine/snap-confine-args-test.c b/cmd/snap-confine/snap-confine-args-test.c index ea57d97aca..34216a6b7f 100644 --- a/cmd/snap-confine/snap-confine-args-test.c +++ b/cmd/snap-confine/snap-confine-args-test.c @@ -23,57 +23,6 @@ #include <glib.h> -/** - * Create an argc + argv pair out of a NULL terminated argument list. - **/ -static void - __attribute__((sentinel)) test_argc_argv(int *argcp, char ***argvp, ...) -{ - int argc = 0; - char **argv = NULL; - g_test_queue_free(argv); - - va_list ap; - va_start(ap, argvp); - const char *arg; - do { - arg = va_arg(ap, const char *); - // XXX: yeah, wrong way but the worse that can happen is for test to fail - argv = realloc(argv, sizeof(const char **) * (argc + 1)); - g_assert_nonnull(argv); - if (arg != NULL) { - char *arg_copy = sc_strdup(arg); - g_test_queue_free(arg_copy); - argv[argc] = arg_copy; - argc += 1; - } else { - argv[argc] = NULL; - } - } while (arg != NULL); - va_end(ap); - - *argcp = argc; - *argvp = argv; -} - -static void test_test_argc_argv(void) -{ - // Check that test_argc_argv() correctly stores data - int argc; - char **argv; - - test_argc_argv(&argc, &argv, NULL); - g_assert_cmpint(argc, ==, 0); - g_assert_null(argv[0]); - - test_argc_argv(&argc, &argv, "zero", "one", "two", NULL); - g_assert_cmpint(argc, ==, 3); - g_assert_cmpstr(argv[0], ==, "zero"); - g_assert_cmpstr(argv[1], ==, "one"); - g_assert_cmpstr(argv[2], ==, "two"); - g_assert_null(argv[3]); -} - static void test_sc_nonfatal_parse_args__typical(void) { // Test that typical invocation of snap-confine is parsed correctly. @@ -239,6 +188,7 @@ static void test_sc_nonfatal_parse_args__evil_input(void) g_assert_null(args); g_assert_cmpstr(sc_error_msg(err), ==, "cannot parse arguments, argcp or argvp is NULL"); + sc_cleanup_error(&err); int argc; char **argv; @@ -252,6 +202,7 @@ static void test_sc_nonfatal_parse_args__evil_input(void) g_assert_null(args); g_assert_cmpstr(sc_error_msg(err), ==, "cannot parse arguments, argc is zero or argv is NULL"); + sc_cleanup_error(&err); // NULL argv[i] attack test_argc_argv(&argc, &argv, @@ -451,7 +402,6 @@ static void test_sc_nonfatal_parse_args__base_snap__twice(void) static void __attribute__((constructor)) init(void) { - g_test_add_func("/args/test_argc_argv", test_test_argc_argv); g_test_add_func("/args/sc_cleanup_args", test_sc_cleanup_args); g_test_add_func("/args/sc_nonfatal_parse_args/typical", test_sc_nonfatal_parse_args__typical); diff --git a/cmd/snap-confine/snap-confine-args.c b/cmd/snap-confine/snap-confine-args.c index f660a8aef9..a44c9ce89f 100644 --- a/cmd/snap-confine/snap-confine-args.c +++ b/cmd/snap-confine/snap-confine-args.c @@ -21,6 +21,7 @@ #include "../libsnap-confine-private/utils.h" #include "../libsnap-confine-private/string-utils.h" +#include "../libsnap-confine-private/test-utils.h" struct sc_args { // The security tag that the application is intended to run with diff --git a/cmd/snap-confine/snap-confine-invocation-test.c b/cmd/snap-confine/snap-confine-invocation-test.c new file mode 100644 index 0000000000..6f1369c2a4 --- /dev/null +++ b/cmd/snap-confine/snap-confine-invocation-test.c @@ -0,0 +1,162 @@ +/* + * 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/>. + * + */ + +#include "snap-confine-invocation.h" +#include "snap-confine-args.h" +#include "snap-confine-invocation.c" + +#include "../libsnap-confine-private/cleanup-funcs.h" +#include "../libsnap-confine-private/test-utils.h" + +#include <stdarg.h> + +#include <glib.h> + +static struct sc_args *test_prepare_args(const char *base, const char *tag) { + struct sc_args *args = NULL; + sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + int argc; + char **argv; + + test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine", "--base", (base != NULL) ? base : "core", + (tag != NULL) ? tag : "snap.foo.app", "/usr/lib/snapd/snap-exec", NULL); + args = sc_nonfatal_parse_args(&argc, &argv, &err); + g_assert_null(err); + g_assert_nonnull(args); + + return args; +} + +static void test_sc_invocation_basic(void) { + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("core", NULL); + + sc_invocation inv SC_CLEANUP(sc_cleanup_invocation); + ; + sc_init_invocation(&inv, args, "foo"); + + g_assert_cmpstr(inv.base_snap_name, ==, "core"); + g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec"); + g_assert_cmpstr(inv.orig_base_snap_name, ==, "core"); + g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/core/current"); + g_assert_cmpstr(inv.security_tag, ==, "snap.foo.app"); + g_assert_cmpstr(inv.snap_instance, ==, "foo"); + g_assert_cmpstr(inv.snap_name, ==, "foo"); + g_assert_false(inv.classic_confinement); + /* derived later */ + g_assert_false(inv.is_normal_mode); +} + +static void test_sc_invocation_instance_key(void) { + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("core", "snap.foo_bar.app"); + + sc_invocation inv SC_CLEANUP(sc_cleanup_invocation); + ; + sc_init_invocation(&inv, args, "foo_bar"); + + // Check the error that we've got + g_assert_cmpstr(inv.snap_instance, ==, "foo_bar"); + g_assert_cmpstr(inv.snap_name, ==, "foo"); + g_assert_cmpstr(inv.orig_base_snap_name, ==, "core"); + g_assert_cmpstr(inv.security_tag, ==, "snap.foo_bar.app"); + g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec"); + g_assert_false(inv.classic_confinement); + g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/core/current"); + g_assert_cmpstr(inv.base_snap_name, ==, "core"); + /* derived later */ + g_assert_false(inv.is_normal_mode); +} + +static void test_sc_invocation_base_name(void) { + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("base-snap", NULL); + + sc_invocation inv SC_CLEANUP(sc_cleanup_invocation); + sc_init_invocation(&inv, args, "foo"); + + g_assert_cmpstr(inv.base_snap_name, ==, "base-snap"); + g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec"); + g_assert_cmpstr(inv.orig_base_snap_name, ==, "base-snap"); + g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/base-snap/current"); + g_assert_cmpstr(inv.security_tag, ==, "snap.foo.app"); + g_assert_cmpstr(inv.snap_instance, ==, "foo"); + g_assert_cmpstr(inv.snap_name, ==, "foo"); + g_assert_false(inv.classic_confinement); + /* derived later */ + g_assert_false(inv.is_normal_mode); +} + +static void test_sc_invocation_bad_instance_name(void) { + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args(NULL, NULL); + + if (g_test_subprocess()) { + sc_invocation inv SC_CLEANUP(sc_cleanup_invocation) = {0}; + sc_init_invocation(&inv, args, "foo_bar_bar_bar"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("snap instance name can contain only one underscore\n"); +} + +static void test_sc_invocation_classic(void) { + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = NULL; + sc_error *err SC_CLEANUP(sc_cleanup_error) = NULL; + int argc; + char **argv = NULL; + + test_argc_argv(&argc, &argv, "/usr/lib/snapd/snap-confine", "--classic", "--base", "core", "snap.foo-classic.app", + "/usr/lib/snapd/snap-exec", NULL); + args = sc_nonfatal_parse_args(&argc, &argv, &err); + g_assert_null(err); + g_assert_nonnull(args); + + sc_invocation inv SC_CLEANUP(sc_cleanup_invocation) = {0}; + sc_init_invocation(&inv, args, "foo-classic"); + + g_assert_cmpstr(inv.base_snap_name, ==, "core"); + g_assert_cmpstr(inv.executable, ==, "/usr/lib/snapd/snap-exec"); + g_assert_cmpstr(inv.orig_base_snap_name, ==, "core"); + g_assert_cmpstr(inv.rootfs_dir, ==, SNAP_MOUNT_DIR "/core/current"); + g_assert_cmpstr(inv.security_tag, ==, "snap.foo-classic.app"); + g_assert_cmpstr(inv.snap_instance, ==, "foo-classic"); + g_assert_cmpstr(inv.snap_name, ==, "foo-classic"); + g_assert_true(inv.classic_confinement); +} + +static void test_sc_invocation_tag_name_mismatch(void) { + struct sc_args *args SC_CLEANUP(sc_cleanup_args) = test_prepare_args("core", "snap.foo.app"); + + if (g_test_subprocess()) { + sc_invocation inv SC_CLEANUP(sc_cleanup_invocation); + ; + sc_init_invocation(&inv, args, "foo-not-foo"); + return; + } + + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("security tag snap.foo.app not allowed\n"); +} + +static void __attribute__((constructor)) init(void) { + g_test_add_func("/invocation/bad_instance_name", test_sc_invocation_bad_instance_name); + g_test_add_func("/invocation/base_name", test_sc_invocation_base_name); + g_test_add_func("/invocation/basic", test_sc_invocation_basic); + g_test_add_func("/invocation/classic", test_sc_invocation_classic); + g_test_add_func("/invocation/instance_key", test_sc_invocation_instance_key); + g_test_add_func("/invocation/tag_name_mismatch", test_sc_invocation_tag_name_mismatch); +} diff --git a/cmd/snap-repair/runner.go b/cmd/snap-repair/runner.go index 91f9fe9770..335f020dd3 100644 --- a/cmd/snap-repair/runner.go +++ b/cmd/snap-repair/runner.go @@ -776,7 +776,7 @@ func (run *Runner) Applicable(headers map[string]interface{}) bool { if err != nil { return false } - if len(archs) != 0 && !strutil.ListContains(archs, arch.UbuntuArchitecture()) { + if len(archs) != 0 && !strutil.ListContains(archs, arch.DpkgArchitecture()) { return false } brandModel := fmt.Sprintf("%s/%s", run.state.Device.Brand, run.state.Device.Model) diff --git a/cmd/snap-repair/runner_test.go b/cmd/snap-repair/runner_test.go index 279854c6af..2afdf885af 100644 --- a/cmd/snap-repair/runner_test.go +++ b/cmd/snap-repair/runner_test.go @@ -782,10 +782,10 @@ func (s *runnerSuite) TestApplicable(c *C) { {map[string]interface{}{"series": []interface{}{"18", "16"}}, true}, {map[string]interface{}{"series": "18"}, false}, {map[string]interface{}{"series": []interface{}{18}}, false}, - {map[string]interface{}{"architectures": []interface{}{arch.UbuntuArchitecture()}}, true}, + {map[string]interface{}{"architectures": []interface{}{arch.DpkgArchitecture()}}, true}, {map[string]interface{}{"architectures": []interface{}{"other-arch"}}, false}, - {map[string]interface{}{"architectures": []interface{}{"other-arch", arch.UbuntuArchitecture()}}, true}, - {map[string]interface{}{"architectures": arch.UbuntuArchitecture()}, false}, + {map[string]interface{}{"architectures": []interface{}{"other-arch", arch.DpkgArchitecture()}}, true}, + {map[string]interface{}{"architectures": arch.DpkgArchitecture()}, false}, {map[string]interface{}{"models": []interface{}{"my-brand/my-model"}}, true}, {map[string]interface{}{"models": []interface{}{"other-brand/other-model"}}, false}, {map[string]interface{}{"models": []interface{}{"other-brand/other-model", "my-brand/my-model"}}, true}, diff --git a/cmd/snap-seccomp/export_test.go b/cmd/snap-seccomp/export_test.go index c29030d9f7..48d23d846d 100644 --- a/cmd/snap-seccomp/export_test.go +++ b/cmd/snap-seccomp/export_test.go @@ -26,19 +26,19 @@ var ( GoSeccompFeatures = goSeccompFeatures ) -func MockArchUbuntuArchitecture(f func() string) (restore func()) { - realArchUbuntuArchitecture := archUbuntuArchitecture - archUbuntuArchitecture = f +func MockArchDpkgArchitecture(f func() string) (restore func()) { + realArchDpkgArchitecture := archDpkgArchitecture + archDpkgArchitecture = f return func() { - archUbuntuArchitecture = realArchUbuntuArchitecture + archDpkgArchitecture = realArchDpkgArchitecture } } -func MockArchUbuntuKernelArchitecture(f func() string) (restore func()) { - realArchUbuntuKernelArchitecture := archUbuntuKernelArchitecture - archUbuntuKernelArchitecture = f +func MockArchDpkgKernelArchitecture(f func() string) (restore func()) { + realArchDpkgKernelArchitecture := archDpkgKernelArchitecture + archDpkgKernelArchitecture = f return func() { - archUbuntuKernelArchitecture = realArchUbuntuKernelArchitecture + archDpkgKernelArchitecture = realArchDpkgKernelArchitecture } } diff --git a/cmd/snap-seccomp/main.go b/cmd/snap-seccomp/main.go index 8fcf6df74d..81670ce8b2 100644 --- a/cmd/snap-seccomp/main.go +++ b/cmd/snap-seccomp/main.go @@ -426,10 +426,10 @@ var seccompResolver = map[string]uint64{ "PTRACE_CONT": C.PTRACE_CONT, } -// UbuntuArchToScmpArch takes a dpkg architecture and converts it to +// DpkgArchToScmpArch takes a dpkg architecture and converts it to // the seccomp.ScmpArch as used in the libseccomp-golang library -func UbuntuArchToScmpArch(ubuntuArch string) seccomp.ScmpArch { - switch ubuntuArch { +func DpkgArchToScmpArch(dpkgArch string) seccomp.ScmpArch { + switch dpkgArch { case "amd64": return seccomp.ArchAMD64 case "arm64": @@ -447,7 +447,7 @@ func UbuntuArchToScmpArch(ubuntuArch string) seccomp.ScmpArch { case "s390x": return seccomp.ArchS390X } - panic(fmt.Sprintf("cannot map ubuntu arch %q to a seccomp arch", ubuntuArch)) + panic(fmt.Sprintf("cannot map dpkg arch %q to a seccomp arch", dpkgArch)) } // important for unit testing @@ -631,13 +631,13 @@ func parseLine(line string, secFilter *seccomp.ScmpFilter) error { // used to mock in tests var ( - archUbuntuArchitecture = arch.UbuntuArchitecture - archUbuntuKernelArchitecture = arch.UbuntuKernelArchitecture + archDpkgArchitecture = arch.DpkgArchitecture + archDpkgKernelArchitecture = arch.DpkgKernelArchitecture ) var ( - ubuntuArchitecture = archUbuntuArchitecture() - ubuntuKernelArchitecture = archUbuntuKernelArchitecture() + dpkgArchitecture = archDpkgArchitecture() + dpkgKernelArchitecture = archDpkgKernelArchitecture() ) // For architectures that support a compat architecture, when the @@ -653,8 +653,8 @@ func addSecondaryArches(secFilter *seccomp.ScmpFilter) error { // add a compat architecture for some architectures that // support it, e.g. on amd64 kernel and userland, we add // compat i386 syscalls. - if ubuntuArchitecture == ubuntuKernelArchitecture { - switch archUbuntuArchitecture() { + if dpkgArchitecture == dpkgKernelArchitecture { + switch archDpkgArchitecture() { case "amd64": compatArch = seccomp.ArchX86 case "arm64": @@ -672,7 +672,7 @@ func addSecondaryArches(secFilter *seccomp.ScmpFilter) error { // snaps. While unusual from a traditional Linux distribution // perspective, certain classes of embedded devices are known // to use this configuration. - compatArch = UbuntuArchToScmpArch(archUbuntuKernelArchitecture()) + compatArch = DpkgArchToScmpArch(archDpkgKernelArchitecture()) } if compatArch != seccomp.ArchInvalid { diff --git a/cmd/snap-seccomp/main_test.go b/cmd/snap-seccomp/main_test.go index 6385834e72..75262367fc 100644 --- a/cmd/snap-seccomp/main_test.go +++ b/cmd/snap-seccomp/main_test.go @@ -191,7 +191,7 @@ func (s *snapSeccompSuite) SetUpSuite(c *C) { // Build 32bit runner on amd64 to test non-native syscall handling. // Ideally we would build for ppc64el->powerpc and arm64->armhf but // it seems tricky to find the right gcc-multilib for this. - if arch.UbuntuArchitecture() == "amd64" && s.canCheckCompatArch { + if arch.DpkgArchitecture() == "amd64" && s.canCheckCompatArch { cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...) cmd.Args = append(cmd.Args, "-m32") for i, k := range cmd.Args { @@ -274,7 +274,7 @@ restart_syscall // compiler that can produce the required binaries. Currently // we only test amd64 running i386 here. if syscallArch != "native" { - syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.UbuntuArchToScmpArch(syscallArch)) + syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.DpkgArchToScmpArch(syscallArch)) c.Assert(err, IsNil) switch syscallArch { @@ -825,10 +825,10 @@ func (s *snapSeccompSuite) TestCompatArchWorks(c *C) { // https://github.com/seccomp/libseccomp/issues/86 // // This means we can not just - // main.MockArchUbuntuArchitecture(t.arch) + // main.MockArchDpkgArchitecture(t.arch) // here because on endian mismatch the arch will *not* be // added - if arch.UbuntuArchitecture() == t.arch { + if arch.DpkgArchitecture() == t.arch { s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected) } } diff --git a/daemon/api_test.go b/daemon/api_test.go index 693f023e80..84073438b8 100644 --- a/daemon/api_test.go +++ b/daemon/api_test.go @@ -7125,7 +7125,7 @@ func (s *apiSuite) TestErrToResponseNoSnapsDoesNotPanic(c *check.C) { func (s *apiSuite) TestErrToResponseForRevisionNotAvailable(c *check.C) { si := &snapInstruction{Action: "frobble", Snaps: []string{"foo"}} - thisArch := arch.UbuntuArchitecture() + thisArch := arch.DpkgArchitecture() err := &store.RevisionNotAvailableError{ Action: "install", diff --git a/daemon/response.go b/daemon/response.go index b10937f522..c542cba82b 100644 --- a/daemon/response.go +++ b/daemon/response.go @@ -420,7 +420,7 @@ func SnapRevisionNotAvailable(snapName string, rnaErr *store.RevisionNotAvailabl kind := errorKindSnapRevisionNotAvailable msg := rnaErr.Error() if len(rnaErr.Releases) != 0 && rnaErr.Channel != "" { - thisArch := arch.UbuntuArchitecture() + thisArch := arch.DpkgArchitecture() values := map[string]interface{}{ "snap-name": snapName, "action": rnaErr.Action, diff --git a/dirs/dirs.go b/dirs/dirs.go index c022830653..a99e5687d9 100644 --- a/dirs/dirs.go +++ b/dirs/dirs.go @@ -206,6 +206,16 @@ func isInsideBaseSnap() (bool, error) { return err == nil, err } +// SnapBlobDirUnder returns the path to the snap blob dir under rootdir. +func SnapBlobDirUnder(rootdir string) string { + return filepath.Join(rootdir, snappyDir, "snaps") +} + +// SnapSeedDirUnder returns the path to the snap seed dir under rootdir. +func SnapSeedDirUnder(rootdir string) string { + return filepath.Join(rootdir, snappyDir, "seed") +} + // SetRootDir allows settings a new global root directory, this is useful // for e.g. chroot operations func SetRootDir(rootdir string) { @@ -231,7 +241,7 @@ func SetRootDir(rootdir string) { SnapSeccompDir = filepath.Join(rootdir, snappyDir, "seccomp", "bpf") SnapMountPolicyDir = filepath.Join(rootdir, snappyDir, "mount") SnapMetaDir = filepath.Join(rootdir, snappyDir, "meta") - SnapBlobDir = filepath.Join(rootdir, snappyDir, "snaps") + SnapBlobDir = SnapBlobDirUnder(rootdir) SnapDesktopFilesDir = filepath.Join(rootdir, snappyDir, "desktop", "applications") SnapRunDir = filepath.Join(rootdir, "/run/snapd") SnapRunNsDir = filepath.Join(SnapRunDir, "/ns") @@ -255,7 +265,7 @@ func SetRootDir(rootdir string) { SnapCommandsDB = filepath.Join(SnapCacheDir, "commands.db") SnapAuxStoreInfoDir = filepath.Join(SnapCacheDir, "aux") - SnapSeedDir = filepath.Join(rootdir, snappyDir, "seed") + SnapSeedDir = SnapSeedDirUnder(rootdir) SnapDeviceDir = filepath.Join(rootdir, snappyDir, "device") SnapRepairDir = filepath.Join(rootdir, snappyDir, "repair") diff --git a/dirs/dirs_test.go b/dirs/dirs_test.go index 6c47dd7f12..e4b2084c62 100644 --- a/dirs/dirs_test.go +++ b/dirs/dirs_test.go @@ -156,3 +156,13 @@ func (s *DirsTestSuite) TestIsCompleteShSymlink(c *C) { c.Check(dirs.IsCompleteShSymlink("/etc/passwd"), Equals, false) c.Check(dirs.IsCompleteShSymlink("/does-not-exist"), Equals, false) } + +func (s *DirsTestSuite) TestUnder(c *C) { + dirs.SetRootDir("/nowhere") + defer dirs.SetRootDir("") + + rootdir := "/other-root" + + c.Check(dirs.SnapBlobDirUnder(rootdir), Equals, "/other-root/var/lib/snapd/snaps") + c.Check(dirs.SnapSeedDirUnder(rootdir), Equals, "/other-root/var/lib/snapd/seed") +} diff --git a/errtracker/errtracker.go b/errtracker/errtracker.go index d55643ec63..5172148183 100644 --- a/errtracker/errtracker.go +++ b/errtracker/errtracker.go @@ -429,7 +429,7 @@ func report(errMsg, dupSig string, extra map[string]string) (string, error) { } report := map[string]string{ - "Architecture": arch.UbuntuArchitecture(), + "Architecture": arch.DpkgArchitecture(), "SnapdVersion": SnapdVersion, "DistroRelease": distroRelease(), "HostSnapdBuildID": hostBuildID, diff --git a/errtracker/errtracker_test.go b/errtracker/errtracker_test.go index 33613577c6..cf01819b47 100644 --- a/errtracker/errtracker_test.go +++ b/errtracker/errtracker_test.go @@ -167,7 +167,7 @@ func (s *ErrtrackerTestSuite) TestReport(c *C) { "KernelVersion": osutil.KernelVersion(), "ErrorMessage": "failed to do stuff", "DuplicateSignature": "[failed to do stuff]", - "Architecture": arch.UbuntuArchitecture(), + "Architecture": arch.DpkgArchitecture(), "DidSnapdReExec": "yes", "ProblemType": "Snap", @@ -312,7 +312,7 @@ func (s *ErrtrackerTestSuite) TestReportRepair(c *C) { "SnapdVersion": "some-snapd-version", "Date": "Fri Feb 17 09:51:00 2017", "KernelVersion": osutil.KernelVersion(), - "Architecture": arch.UbuntuArchitecture(), + "Architecture": arch.DpkgArchitecture(), "DidSnapdReExec": "yes", "ProblemType": "Repair", diff --git a/httputil/useragent.go b/httputil/useragent.go index 20c86cb2c8..4c1dc15e5e 100644 --- a/httputil/useragent.go +++ b/httputil/useragent.go @@ -64,7 +64,7 @@ func SetUserAgentFromVersion(version string, extraProds ...string) (restore func // assumption checks out in practice, q.v. https://github.com/zyga/os-release-zoo userAgent = fmt.Sprintf("snapd/%v (%s)%s %s/%s (%s) linux/%s", version, strings.Join(extras, "; "), extraProdStr, release.ReleaseInfo.ID, - release.ReleaseInfo.VersionID, string(arch.UbuntuArchitecture()), + release.ReleaseInfo.VersionID, string(arch.DpkgArchitecture()), sanitizeKernelVersion(osutil.KernelVersion())) return func() { userAgent = origUserAgent diff --git a/image/export_test.go b/image/export_test.go index e178dc4f59..ced1bada1d 100644 --- a/image/export_test.go +++ b/image/export_test.go @@ -31,7 +31,6 @@ func MockToolingStore(sto Store) *ToolingStore { var ( LocalSnaps = localSnaps DecodeModelAssertion = decodeModelAssertion - DownloadUnpackGadget = downloadUnpackGadget SetupSeed = setupSeed InstallCloudConfig = installCloudConfig SnapChannel = snapChannel diff --git a/image/image.go b/image/image.go index 4da7983689..cf5246dead 100644 --- a/image/image.go +++ b/image/image.go @@ -32,7 +32,7 @@ import ( "github.com/snapcore/snapd/asserts" "github.com/snapcore/snapd/asserts/snapasserts" "github.com/snapcore/snapd/asserts/sysdb" - "github.com/snapcore/snapd/bootloader" + "github.com/snapcore/snapd/boot" "github.com/snapcore/snapd/dirs" "github.com/snapcore/snapd/osutil" "github.com/snapcore/snapd/release" @@ -229,9 +229,9 @@ func Prepare(opts *Options) error { } if !opts.Classic { - // unpacking the gadget for core models - if err := downloadUnpackGadget(tsto, model, opts, local); err != nil { - return err + // create directory for later unpacking the gadget in + if err := os.MkdirAll(opts.GadgetUnpackDir, 0755); err != nil { + return fmt.Errorf("cannot create gadget unpack dir %q: %s", opts.GadgetUnpackDir, err) } } @@ -275,6 +275,11 @@ func snapChannel(name string, model *asserts.Model, opts *Options, local *localI // fallback to default channel snapChannel = opts.Channel } + if snapChannel != "" { + if _, err := channel.ParseVerbatim(snapChannel, "-"); err != nil { + return "", fmt.Errorf("cannot use option channel for snap %q: %v", name, err) + } + } // consider snap types that can be pinned to a track by the model var pinnedTrack string var kind string @@ -286,58 +291,21 @@ func snapChannel(name string, model *asserts.Model, opts *Options, local *localI kind = "kernel" pinnedTrack = model.KernelTrack() } - if pinnedTrack != "" { - ch, err := makeChannelFromTrack(kind, pinnedTrack, snapChannel) - if err != nil { - return "", err - } - snapChannel = ch - + ch, err := channel.ResolveLocked(pinnedTrack, snapChannel) + if err == channel.ErrLockedTrackSwitch { + return "", fmt.Errorf("channel %q for %s has a track incompatible with the track from model assertion: %s", snapChannel, kind, pinnedTrack) } - return snapChannel, nil -} - -func makeChannelFromTrack(what, track, snapChannel string) (string, error) { - mch, err := channel.Parse(track, "") if err != nil { - return "", fmt.Errorf("cannot use track %q for %s from model assertion: %v", track, what, err) - } - if snapChannel != "" { - ch, err := channel.ParseVerbatim(snapChannel, "") - if err != nil { - return "", fmt.Errorf("cannot parse channel %q for %s", snapChannel, what) - } - if ch.Track != "" && ch.Track != mch.Track { - return "", fmt.Errorf("channel %q for %s has a track incompatible with the track from model assertion: %s", snapChannel, what, track) - } - mch.Risk = ch.Risk + return "", err } - return mch.Clean().String(), nil + return ch, nil } -func downloadUnpackGadget(tsto *ToolingStore, model *asserts.Model, opts *Options, local *localInfos) error { - if err := os.MkdirAll(opts.GadgetUnpackDir, 0755); err != nil { - return fmt.Errorf("cannot create gadget unpack dir %q: %s", opts.GadgetUnpackDir, err) - } - - gadgetName := model.Gadget() - gadgetChannel, err := snapChannel(gadgetName, model, opts, local) - if err != nil { - return err - } - - dlOpts := &DownloadOptions{ - TargetDir: opts.GadgetUnpackDir, - Channel: gadgetChannel, - } - snapFn, _, err := acquireSnap(tsto, gadgetName, dlOpts, local) - if err != nil { - return err - } +func unpackGadget(gadgetFname, gadgetUnpackDir string) error { // FIXME: jumping through layers here, we need to make // unpack part of the container interface (again) - snap := squashfs.New(snapFn) - return snap.Unpack("*", opts.GadgetUnpackDir) + snap := squashfs.New(gadgetFname) + return snap.Unpack("*", gadgetUnpackDir) } func acquireSnap(tsto *ToolingStore, name string, dlOpts *DownloadOptions, local *localInfos) (downloadedSnap string, info *snap.Info, err error) { @@ -586,14 +554,12 @@ 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, dirs.GlobalRootDir); err != nil { + // unpacking the gadget for core models + if err := unpackGadget(seed.gadgetFname, opts.GadgetUnpackDir); err != nil { return err } - if err := setBootvars(seed.downloadedSnapsInfoForBootConfig, model); err != nil { + if err := boot.MakeBootable(model, opts.RootDir, seed.downloadedSnapsInfoForBootConfig, opts.GadgetUnpackDir); err != nil { return err } @@ -639,6 +605,7 @@ type imageSeed struct { seen map[string]bool locals []string downloadedSnapsInfoForBootConfig map[string]*snap.Info + gadgetFname string needsCore []string needsCore16 []string @@ -722,19 +689,13 @@ func (s *imageSeed) add(snapName string) error { // kernel/os/model.base are required for booting on core if !opts.Classic && (typ == snap.TypeKernel || name == s.baseName) { - dst := filepath.Join(dirs.SnapBlobDir, filepath.Base(fn)) - // construct a relative symlink from the blob dir - // to the seed file - relSymlink, err := filepath.Rel(dirs.SnapBlobDir, fn) - if err != nil { - return fmt.Errorf("cannot build symlink: %v", err) - } - if err := os.Symlink(relSymlink, dst); err != nil { - return err - } // store the snap.Info for kernel/os/base so - // that the bootload can DTRT - s.downloadedSnapsInfoForBootConfig[dst] = info + // that boot.MakeBootable can DTRT + s.downloadedSnapsInfoForBootConfig[fn] = info + } + // remember the gadget for unpacking later + if !opts.Classic && typ == snap.TypeGadget { + s.gadgetFname = fn } s.entries = append(s.entries, seedEntry{ @@ -805,79 +766,6 @@ func (s *imageSeed) seedYaml() *seed.Seed16 { return &seedYaml } -func setBootvars(downloadedSnapsInfoForBootConfig map[string]*snap.Info, model *asserts.Model) error { - if len(downloadedSnapsInfoForBootConfig) != 2 { - return fmt.Errorf("setBootvars can only be called with exactly one kernel and exactly one core/base boot info: %v", downloadedSnapsInfoForBootConfig) - } - - // 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("", &bootloader.Options{ - PrepareImageTime: true, - }) - if err != nil { - return fmt.Errorf("cannot set kernel/core boot variables: %s", err) - } - - snaps, err := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*.snap")) - if len(snaps) == 0 || err != nil { - return fmt.Errorf("internal error: cannot find core/kernel snap") - } - - m := map[string]string{ - "snap_mode": "", - "snap_try_core": "", - "snap_try_kernel": "", - } - if model.DisplayName() != "" { - m["snap_menuentry"] = model.DisplayName() - } - - for _, fn := range snaps { - bootvar := "" - - info := downloadedSnapsInfoForBootConfig[fn] - if info == nil { - // this should never happen, if it does print some - // debug info - keys := make([]string, 0, len(downloadedSnapsInfoForBootConfig)) - for k := range downloadedSnapsInfoForBootConfig { - keys = append(keys, k) - } - return fmt.Errorf("cannot get download info for snap %s, available infos: %v", fn, keys) - } - switch info.GetType() { - case snap.TypeOS, snap.TypeBase: - bootvar = "snap_core" - case snap.TypeKernel: - bootvar = "snap_kernel" - if err := extractKernelAssets(bloader, fn, info, model); err != nil { - return err - } - } - - if bootvar != "" { - name := filepath.Base(fn) - m[bootvar] = name - } - } - if err := bloader.SetBootVars(m); err != nil { - return err - } - - return nil -} - -func extractKernelAssets(bootloader bootloader.Bootloader, snapPath string, info *snap.Info, model *asserts.Model) error { - snapf, err := snap.Open(snapPath) - if err != nil { - return err - } - - return bootloader.ExtractKernelAssets(info, snapf) -} - func copyLocalSnapFile(snapPath, targetDir string, info *snap.Info) (dstPath string, err error) { dst := filepath.Join(targetDir, filepath.Base(info.MountFile())) return dst, osutil.CopyFile(snapPath, dst, 0) diff --git a/image/image_test.go b/image/image_test.go index a421c9dae8..d10d0e7d1d 100644 --- a/image/image_test.go +++ b/image/image_test.go @@ -463,79 +463,12 @@ func (s *imageSuite) TestHappyDecodeModelAssertion(c *C) { c.Check(a.Type(), Equals, asserts.ModelType) } -func (s *imageSuite) TestMissingGadgetUnpackDir(c *C) { - err := image.DownloadUnpackGadget(s.tsto, s.model, &image.Options{}, nil) - c.Assert(err, ErrorMatches, `cannot create gadget unpack dir "": mkdir : no such file or directory`) -} - -func (s *imageSuite) TestDownloadUnpackGadget(c *C) { - files := [][]string{ - {"subdir/canary.txt", "I'm a canary"}, - } - s.MakeAssertedSnap(c, packageGadget, files, snap.R(99), "canonical") - - gadgetUnpackDir := filepath.Join(c.MkDir(), "gadget-unpack-dir") - opts := &image.Options{ - GadgetUnpackDir: gadgetUnpackDir, - } - local, err := image.LocalSnaps(s.tsto, opts) - c.Assert(err, IsNil) - - err = image.DownloadUnpackGadget(s.tsto, s.model, opts, local) - c.Assert(err, IsNil) - - // verify the right data got unpacked - for _, t := range []struct{ file, content string }{ - {"meta/snap.yaml", packageGadget}, - {files[0][0], files[0][1]}, - } { - fn := filepath.Join(gadgetUnpackDir, t.file) - c.Check(fn, testutil.FileEquals, t.content) - } -} - -func (s *imageSuite) TestDownloadUnpackGadgetFromTrack(c *C) { - s.MakeAssertedSnap(c, packageGadget, nil, snap.R(1818), "canonical") - - model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ - - "architecture": "amd64", - "gadget": "pc=18", - "kernel": "pc-kernel=18", - }) - - gadgetUnpackDir := filepath.Join(c.MkDir(), "gadget-unpack-dir") - opts := &image.Options{ - GadgetUnpackDir: gadgetUnpackDir, - } - local, err := image.LocalSnaps(s.tsto, opts) - c.Assert(err, IsNil) - - err = image.DownloadUnpackGadget(s.tsto, model, opts, local) - c.Assert(err, IsNil) - - c.Check(s.storeActions, HasLen, 1) - c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ - Action: "download", - Channel: "18/stable", - InstanceName: "pc", - }) - -} - func (s *imageSuite) setupSnaps(c *C, gadgetUnpackDir string, publishers map[string]string) { - if gadgetUnpackDir != "" { - err := os.MkdirAll(gadgetUnpackDir, 0755) - c.Assert(err, IsNil) - err = ioutil.WriteFile(filepath.Join(gadgetUnpackDir, "grub.conf"), nil, 0644) - c.Assert(err, IsNil) - } - if _, ok := publishers["pc"]; ok { - s.MakeAssertedSnap(c, packageGadget, [][]string{{"grub.cfg", "I'm a grub.cfg"}}, snap.R(1), publishers["pc"]) + s.MakeAssertedSnap(c, packageGadget, [][]string{{"grub.conf", ""}, {"grub.cfg", "I'm a grub.cfg"}}, snap.R(1), publishers["pc"]) } if _, ok := publishers["pc18"]; ok { - s.MakeAssertedSnap(c, packageGadgetWithBase, [][]string{{"grub.cfg", "I'm a grub.cfg"}}, snap.R(4), publishers["pc18"]) + s.MakeAssertedSnap(c, packageGadgetWithBase, [][]string{{"grub.conf", ""}, {"grub.cfg", "I'm a grub.cfg"}}, snap.R(4), publishers["pc18"]) } if _, ok := publishers["classic-gadget"]; ok { @@ -577,6 +510,7 @@ func (s *imageSuite) TestSetupSeed(c *C) { defer restore() rootdir := filepath.Join(c.MkDir(), "imageroot") + blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps") seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") seedsnapsdir := filepath.Join(seeddir, "snaps") seedassertsdir := filepath.Join(seeddir, "assertions") @@ -653,7 +587,37 @@ func (s *imageSuite) TestSetupSeed(c *C) { c.Check(m["snap_core"], Equals, "core_3.snap") c.Check(m["snap_menuentry"], Equals, "my display name") + // check symlinks from snap blob dir + kernelInfo := s.AssertedSnapInfo("pc-kernel") + coreInfo := s.AssertedSnapInfo("core") + kernelBlob := filepath.Join(blobdir, filepath.Base(kernelInfo.MountFile())) + dst, err := os.Readlink(kernelBlob) + c.Assert(err, IsNil) + c.Check(dst, Equals, "../seed/snaps/pc-kernel_2.snap") + c.Check(kernelBlob, testutil.FilePresent) + + coreBlob := filepath.Join(blobdir, filepath.Base(coreInfo.MountFile())) + dst, err = os.Readlink(coreBlob) + c.Assert(err, IsNil) + c.Check(dst, Equals, "../seed/snaps/core_3.snap") + c.Check(coreBlob, testutil.FilePresent) + c.Check(s.stderr.String(), Equals, "") + + // check the downloads + c.Check(s.storeActions, HasLen, 4) + c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "core", + }) + c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "pc-kernel", + }) + c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "pc", + }) } func (s *imageSuite) TestSetupSeedLocalCoreBrandKernel(c *C) { @@ -890,6 +854,7 @@ func (s *imageSuite) TestSetupSeedWithBase(c *C) { }) rootdir := filepath.Join(c.MkDir(), "imageroot") + blobdir := filepath.Join(rootdir, "/var/lib/snapd/snaps") gadgetUnpackDir := c.MkDir() s.setupSnaps(c, gadgetUnpackDir, map[string]string{ "core18": "canonical", @@ -955,7 +920,41 @@ func (s *imageSuite) TestSetupSeedWithBase(c *C) { c.Assert(err, IsNil) c.Check(m["snap_core"], Equals, "core18_18.snap") + // check symlinks from snap blob dir + kernelInfo := s.AssertedSnapInfo("pc-kernel") + baseInfo := s.AssertedSnapInfo("core18") + kernelBlob := filepath.Join(blobdir, filepath.Base(kernelInfo.MountFile())) + dst, err := os.Readlink(kernelBlob) + c.Assert(err, IsNil) + c.Check(dst, Equals, "../seed/snaps/pc-kernel_2.snap") + c.Check(kernelBlob, testutil.FilePresent) + + baseBlob := filepath.Join(blobdir, filepath.Base(baseInfo.MountFile())) + dst, err = os.Readlink(baseBlob) + c.Assert(err, IsNil) + c.Check(dst, Equals, "../seed/snaps/core18_18.snap") + c.Check(baseBlob, testutil.FilePresent) + c.Check(s.stderr.String(), Equals, "") + + // check the downloads + c.Check(s.storeActions, HasLen, 5) + c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "snapd", + }) + c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "core18", + }) + c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "pc-kernel", + }) + c.Check(s.storeActions[3], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "pc18", + }) } func (s *imageSuite) TestSetupSeedWithBaseLegacySnap(c *C) { @@ -1313,6 +1312,18 @@ func (s *imageSuite) TestSetupSeedLocalSnapsWithChannels(c *C) { c.Check(l, HasLen, 4) } +func (s *imageSuite) TestMissingGadgetUnpackDir(c *C) { + fn := filepath.Join(c.MkDir(), "model.assertion") + err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) + c.Assert(err, IsNil) + + err = image.Prepare(&image.Options{ + ModelFile: fn, + Channel: "stable", + }) + c.Assert(err, ErrorMatches, `cannot create gadget unpack dir "": mkdir : no such file or directory`) +} + func (s *imageSuite) TestNoLocalParallelSnapInstances(c *C) { fn := filepath.Join(c.MkDir(), "model.assertion") err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) @@ -1442,6 +1453,7 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) { opts := &image.Options{ RootDir: rootdir, GadgetUnpackDir: gadgetUnpackDir, + Channel: "stable", } local, err := image.LocalSnaps(s.tsto, opts) c.Assert(err, IsNil) @@ -1455,9 +1467,10 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) { c.Check(seedYaml.Snaps, HasLen, 3) c.Check(seedYaml.Snaps[0], DeepEquals, &seed.Snap16{ - Name: "core", - SnapID: s.AssertedSnapID("core"), - File: "core_3.snap", + Name: "core", + SnapID: s.AssertedSnapID("core"), + File: "core_3.snap", + Channel: "stable", }) c.Check(seedYaml.Snaps[1], DeepEquals, &seed.Snap16{ Name: "pc-kernel", @@ -1471,6 +1484,24 @@ func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) { File: "pc_1.snap", Channel: "18/stable", }) + + // check the downloads + c.Check(s.storeActions, HasLen, 3) + c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "core", + Channel: "stable", + }) + c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "pc-kernel", + Channel: "18/stable", + }) + c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ + Action: "download", + InstanceName: "pc", + Channel: "18/stable", + }) } func (s *imageSuite) TestSetupSeedWithKernelTrackWithDefaultChannel(c *C) { @@ -2293,6 +2324,11 @@ func (s *imageSuite) TestSnapChannel(c *C) { opts.SnapChannels["pc-kernel"] = "lts/candidate" _, err = image.SnapChannel("pc-kernel", model, opts, local) c.Assert(err, ErrorMatches, `channel "lts/candidate" for kernel has a track incompatible with the track from model assertion: 18`) + + opts.SnapChannels["pc-kernel"] = "track/foo" + _, err = image.SnapChannel("pc-kernel", model, opts, local) + c.Assert(err, ErrorMatches, `cannot use option channel for snap "pc-kernel": invalid risk in channel name: track/foo`) + } func (s *imageSuite) TestSetupSeedLocalSnapd(c *C) { diff --git a/interfaces/seccomp/backend.go b/interfaces/seccomp/backend.go index e0c23e9faa..6108b951bb 100644 --- a/interfaces/seccomp/backend.go +++ b/interfaces/seccomp/backend.go @@ -53,11 +53,11 @@ import ( ) var ( - kernelFeatures = release.SecCompActions - ubuntuKernelArchitecture = arch.UbuntuKernelArchitecture - releaseInfoId = release.ReleaseInfo.ID - releaseInfoVersionId = release.ReleaseInfo.VersionID - requiresSocketcall = requiresSocketcallImpl + kernelFeatures = release.SecCompActions + dpkgKernelArchitecture = arch.DpkgKernelArchitecture + releaseInfoId = release.ReleaseInfo.ID + releaseInfoVersionId = release.ReleaseInfo.VersionID + requiresSocketcall = requiresSocketcallImpl snapSeccompVersionInfo = snapSeccompVersionInfoImpl seccompCompilerLookup = cmd.InternalToolPath @@ -365,7 +365,7 @@ func (b *Backend) SandboxFeatures() []string { // - if the kernel architecture is not any of the above, force the use of // socketcall() func requiresSocketcallImpl(baseSnap string) bool { - switch ubuntuKernelArchitecture() { + switch dpkgKernelArchitecture() { case "i386", "s390x": // glibc sysdeps/unix/sysv/linux/i386/kernel-features.h and // sysdeps/unix/sysv/linux/s390/kernel-features.h added the diff --git a/interfaces/seccomp/backend_test.go b/interfaces/seccomp/backend_test.go index c1b9fe34c1..89e94d6ea3 100644 --- a/interfaces/seccomp/backend_test.go +++ b/interfaces/seccomp/backend_test.go @@ -491,7 +491,7 @@ fi`) func (s *backendSuite) TestRequiresSocketcallByNotNeededArch(c *C) { testArchs := []string{"amd64", "armhf", "arm64", "powerpc", "ppc64el", "unknownDefault"} for _, arch := range testArchs { - restore := seccomp.MockUbuntuKernelArchitecture(func() string { return arch }) + restore := seccomp.MockDpkgKernelArchitecture(func() string { return arch }) defer restore() c.Assert(seccomp.RequiresSocketcall(""), Equals, false) } @@ -500,7 +500,7 @@ func (s *backendSuite) TestRequiresSocketcallByNotNeededArch(c *C) { func (s *backendSuite) TestRequiresSocketcallForceByArch(c *C) { testArchs := []string{"sparc", "sparc64"} for _, arch := range testArchs { - restore := seccomp.MockUbuntuKernelArchitecture(func() string { return arch }) + restore := seccomp.MockDpkgKernelArchitecture(func() string { return arch }) defer restore() c.Assert(seccomp.RequiresSocketcall(""), Equals, true) } @@ -542,7 +542,7 @@ func (s *backendSuite) TestRequiresSocketcallForcedViaUbuntuRelease(c *C) { for _, t := range tests { restore = seccomp.MockReleaseInfoId(t.distro) defer restore() - restore = seccomp.MockUbuntuKernelArchitecture(func() string { return t.arch }) + restore = seccomp.MockDpkgKernelArchitecture(func() string { return t.arch }) defer restore() restore = seccomp.MockReleaseInfoVersionId(t.release) defer restore() @@ -581,7 +581,7 @@ func (s *backendSuite) TestRequiresSocketcallForcedViaKernelVersion(c *C) { } for _, t := range tests { - restore := seccomp.MockUbuntuKernelArchitecture(func() string { return t.arch }) + restore := seccomp.MockDpkgKernelArchitecture(func() string { return t.arch }) defer restore() restore = osutil.MockKernelVersion(t.version) defer restore() @@ -597,7 +597,7 @@ func (s *backendSuite) TestRequiresSocketcallForcedViaBaseSnap(c *C) { // check is reached restore := seccomp.MockReleaseInfoId("other") defer restore() - restore = seccomp.MockUbuntuKernelArchitecture(func() string { return "i386" }) + restore = seccomp.MockDpkgKernelArchitecture(func() string { return "i386" }) defer restore() restore = osutil.MockKernelVersion("4.3") defer restore() @@ -613,7 +613,7 @@ func (s *backendSuite) TestRequiresSocketcallNotForcedViaBaseSnap(c *C) { // check is reached restore := seccomp.MockReleaseInfoId("other") defer restore() - restore = seccomp.MockUbuntuKernelArchitecture(func() string { return "i386" }) + restore = seccomp.MockDpkgKernelArchitecture(func() string { return "i386" }) defer restore() restore = osutil.MockKernelVersion("4.3") defer restore() diff --git a/interfaces/seccomp/export_test.go b/interfaces/seccomp/export_test.go index f5c22d4a2e..36b9ec0e6c 100644 --- a/interfaces/seccomp/export_test.go +++ b/interfaces/seccomp/export_test.go @@ -54,11 +54,11 @@ func MockRequiresSocketcall(f func(string) bool) (restore func()) { } } -func MockUbuntuKernelArchitecture(f func() string) (restore func()) { - old := ubuntuKernelArchitecture - ubuntuKernelArchitecture = f +func MockDpkgKernelArchitecture(f func() string) (restore func()) { + old := dpkgKernelArchitecture + dpkgKernelArchitecture = f return func() { - ubuntuKernelArchitecture = old + dpkgKernelArchitecture = old } } diff --git a/overlord/snapstate/backend/mountunit_test.go b/overlord/snapstate/backend/mountunit_test.go index 7c4821ba6a..5ac596d46f 100644 --- a/overlord/snapstate/backend/mountunit_test.go +++ b/overlord/snapstate/backend/mountunit_test.go @@ -89,6 +89,7 @@ What=/var/lib/snapd/snaps/foo_13.snap Where=%s/foo/13 Type=squashfs Options=nodev,ro,x-gdu.hide +LazyUnmount=yes [Install] WantedBy=multi-user.target diff --git a/overlord/snapstate/check_snap.go b/overlord/snapstate/check_snap.go index cf3ff7f04b..d8ba5f0583 100644 --- a/overlord/snapstate/check_snap.go +++ b/overlord/snapstate/check_snap.go @@ -320,7 +320,7 @@ func validateInfoAndFlags(info *snap.Info, snapst *SnapState, flags Flags) error // verify we have a valid architecture if !arch.IsSupportedArchitecture(info.Architectures) { - return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.UbuntuArchitecture()) + return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.DpkgArchitecture()) } // check assumes diff --git a/overlord/snapstate/check_snap_test.go b/overlord/snapstate/check_snap_test.go index 9bbb7e0c9c..cb263b0f11 100644 --- a/overlord/snapstate/check_snap_test.go +++ b/overlord/snapstate/check_snap_test.go @@ -88,7 +88,7 @@ architectures: err = snapstate.CheckSnap(s.st, "snap-path", "hello", nil, nil, snapstate.Flags{}, nil) - errorMsg := fmt.Sprintf(`snap "hello" supported architectures (yadayada, blahblah) are incompatible with this system (%s)`, arch.UbuntuArchitecture()) + errorMsg := fmt.Sprintf(`snap "hello" supported architectures (yadayada, blahblah) are incompatible with this system (%s)`, arch.DpkgArchitecture()) c.Assert(err.Error(), Equals, errorMsg) } diff --git a/overlord/snapstate/snapstate.go b/overlord/snapstate/snapstate.go index 5592fcdd06..b52df2c7d5 100644 --- a/overlord/snapstate/snapstate.go +++ b/overlord/snapstate/snapstate.go @@ -1206,24 +1206,19 @@ func resolveChannel(st *state.State, snapName, newChannel string, deviceCtx Devi return newChannel, nil } - nch, err := channel.ParseVerbatim(newChannel, "") - if err != nil { - return "", err - } - - if nch.Track == "" { - // channel name is valid and consist of risk level or - // risk/branch only, do the right thing and default to risk (or - // risk/branch) within the pinned track - return pinnedTrack + "/" + newChannel, nil - } - if nch.Track != "" && nch.Track != pinnedTrack { + // channel name is valid and consist of risk level or + // risk/branch only, do the right thing and default to risk (or + // risk/branch) within the pinned track + resChannel, err := channel.ResolveLocked(pinnedTrack, newChannel) + if err == channel.ErrLockedTrackSwitch { // switching to a different track is not allowed - return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, nch.Clean().String()) + return "", fmt.Errorf("cannot switch from %s track %q as specified for the (device) model to %q", which, pinnedTrack, newChannel) } - - return newChannel, nil + if err != nil { + return "", err + } + return resChannel, nil } var errRevisionSwitch = errors.New("cannot switch revision") diff --git a/overlord/snapstate/snapstate_test.go b/overlord/snapstate/snapstate_test.go index 3b4d46c487..4dc18072ca 100644 --- a/overlord/snapstate/snapstate_test.go +++ b/overlord/snapstate/snapstate_test.go @@ -1673,7 +1673,7 @@ func (s *snapmgrTestSuite) TestSwitchKernelTrackForbidden(c *C) { }) _, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}) - c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel/stable"`) + c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`) } func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyIsOK(c *C) { @@ -1730,7 +1730,7 @@ func (s *snapmgrTestSuite) TestSwitchGadgetTrackForbidden(c *C) { }) _, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}) - c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel/stable"`) + c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`) } func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyIsOK(c *C) { @@ -6597,7 +6597,7 @@ func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) { // switching tracks is not ok _, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{}) - c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel/stable"`) + c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`) // no change to the channel is ok _, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{}) @@ -6633,7 +6633,7 @@ func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) { // switching tracks is not ok _, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{}) - c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel/stable"`) + c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`) // no change to the channel is ok _, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{}) @@ -13899,7 +13899,7 @@ version: 1.0`) Channel: "some-channel", } _, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true}) - c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel/stable"`) + c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel"`) } func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchGadget(c *C) { @@ -13930,7 +13930,7 @@ version: 1.0`) Channel: "some-channel", } _, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true}) - c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel/stable"`) + c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel"`) } func (s *snapmgrTestSuite) TestInstallLayoutsChecksFeatureFlag(c *C) { diff --git a/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch b/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch index 0db9d1e8a4..f6e032a0fd 100644 --- a/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch +++ b/packaging/debian-sid/patches/0003-cmd-snap-seccomp-skip-tests-that-use-m32.patch @@ -24,14 +24,14 @@ Signed-off-by: Zygmunt Krynicki <me@zygoon.pl> cmd/snap-seccomp/main_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) -diff --git a/cmd/snap-seccomp/main_test.go b/cmd/snap-seccomp/main_test.go -index d4ca193b2..8977385c2 100644 ---- a/cmd/snap-seccomp/main_test.go -+++ b/cmd/snap-seccomp/main_test.go -@@ -185,6 +185,14 @@ func (s *snapSeccompSuite) SetUpSuite(c *C) { +Index: snapd/cmd/snap-seccomp/main_test.go +=================================================================== +--- snapd.orig/cmd/snap-seccomp/main_test.go ++++ snapd/cmd/snap-seccomp/main_test.go +@@ -192,6 +192,14 @@ func (s *snapSeccompSuite) SetUpSuite(c // Ideally we would build for ppc64el->powerpc and arm64->armhf but // it seems tricky to find the right gcc-multilib for this. - if arch.UbuntuArchitecture() == "amd64" && s.canCheckCompatArch { + if arch.DpkgArchitecture() == "amd64" && s.canCheckCompatArch { + // This test fails on Debian amd64 + // cannot build multi-lib syscall runner: exit status 1 + // In file included from /usr/include/errno.h:25, @@ -43,6 +43,3 @@ index d4ca193b2..8977385c2 100644 cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...) cmd.Args = append(cmd.Args, "-m32") for i, k := range cmd.Args { --- -2.17.1 - diff --git a/snap/channel/channel.go b/snap/channel/channel.go index f62b60fa89..30d652531e 100644 --- a/snap/channel/channel.go +++ b/snap/channel/channel.go @@ -20,6 +20,7 @@ package channel import ( + "errors" "fmt" "strings" @@ -68,7 +69,7 @@ func ParseVerbatim(s string, architecture string) (Channel, error) { } if architecture == "" { - architecture = arch.UbuntuArchitecture() + architecture = arch.DpkgArchitecture() } ch := Channel{ @@ -150,6 +151,16 @@ func (c *Channel) Full() string { return c.String() } +// VerbatimTrackOnly returns whether the channel represents a track only. +func (c *Channel) VerbatimTrackOnly() bool { + return c.Track != "" && c.Risk == "" && c.Branch == "" +} + +// VerbatimRiskOnly returns whether the channel represents a risk only. +func (c *Channel) VerbatimRiskOnly() bool { + return c.Track == "" && c.Risk != "" && c.Branch == "" +} + func riskLevel(risk string) int { for i, r := range channelRisks { if r == risk { @@ -200,3 +211,54 @@ func (c *Channel) Match(c1 *Channel) ChannelMatch { Risk: requestedRiskLevel >= rl1, } } + +// Resolve resolves newChannel wrt channel, this means if newChannel +// is risk/branch only it will preserve the track of channel. It +// assumes that if both are not empty, channel is parseable. +func Resolve(channel, newChannel string) (string, error) { + if newChannel == "" { + return channel, nil + } + if channel == "" { + return newChannel, nil + } + ch, err := ParseVerbatim(channel, "-") + if err != nil { + return "", err + } + p := strings.Split(newChannel, "/") + if strutil.ListContains(channelRisks, p[0]) && ch.Track != "" { + // risk/branch inherits the track if any + return ch.Track + "/" + newChannel, nil + } + return newChannel, nil +} + +var ErrLockedTrackSwitch = errors.New("cannot switch locked track") + +// ResolveLocked resolves newChannel wrt a locked track, newChannel +// can only be risk/branch-only or have the same track, otherwise +// ErrLockedTrackSwitch is returned. +func ResolveLocked(track, newChannel string) (string, error) { + if track == "" { + return newChannel, nil + } + ch, err := ParseVerbatim(track, "-") + if err != nil || !ch.VerbatimTrackOnly() { + return "", fmt.Errorf("invalid locked track: %s", track) + } + if newChannel == "" { + return track, nil + } + trackPrefix := ch.Track + "/" + p := strings.Split(newChannel, "/") + if strutil.ListContains(channelRisks, p[0]) && ch.Track != "" { + // risk/branch inherits the track if any + return trackPrefix + newChannel, nil + } + if newChannel != track && !strings.HasPrefix(newChannel, trackPrefix) { + // the track is locked/pinned + return "", ErrLockedTrackSwitch + } + return newChannel, nil +} diff --git a/snap/channel/channel_test.go b/snap/channel/channel_test.go index 907568e33d..973aac7678 100644 --- a/snap/channel/channel_test.go +++ b/snap/channel/channel_test.go @@ -38,7 +38,7 @@ func (s storeChannelSuite) TestParse(c *C) { ch, err := channel.Parse("stable", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Name: "stable", Track: "", Risk: "stable", @@ -48,7 +48,7 @@ func (s storeChannelSuite) TestParse(c *C) { ch, err = channel.Parse("latest/stable", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Name: "stable", Track: "", Risk: "stable", @@ -58,7 +58,7 @@ func (s storeChannelSuite) TestParse(c *C) { ch, err = channel.Parse("1.0/edge", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Name: "1.0/edge", Track: "1.0", Risk: "edge", @@ -68,7 +68,7 @@ func (s storeChannelSuite) TestParse(c *C) { ch, err = channel.Parse("1.0", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Name: "1.0/stable", Track: "1.0", Risk: "stable", @@ -78,7 +78,7 @@ func (s storeChannelSuite) TestParse(c *C) { ch, err = channel.Parse("1.0/beta/foo", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Name: "1.0/beta/foo", Track: "1.0", Risk: "beta", @@ -88,7 +88,7 @@ func (s storeChannelSuite) TestParse(c *C) { ch, err = channel.Parse("candidate/foo", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Name: "candidate/foo", Track: "", Risk: "candidate", @@ -116,36 +116,54 @@ func (s storeChannelSuite) TestParseVerbatim(c *C) { ch, err := channel.ParseVerbatim("sometrack", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Track: "sometrack", }) + c.Check(ch.VerbatimTrackOnly(), Equals, true) + c.Check(ch.VerbatimRiskOnly(), Equals, false) c.Check(mustParse(c, "sometrack"), DeepEquals, ch.Clean()) ch, err = channel.ParseVerbatim("latest", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Track: "latest", }) + c.Check(ch.VerbatimTrackOnly(), Equals, true) + c.Check(ch.VerbatimRiskOnly(), Equals, false) c.Check(mustParse(c, "latest"), DeepEquals, ch.Clean()) + ch, err = channel.ParseVerbatim("edge", "") + c.Assert(err, IsNil) + c.Check(ch, DeepEquals, channel.Channel{ + Architecture: arch.DpkgArchitecture(), + Risk: "edge", + }) + c.Check(ch.VerbatimTrackOnly(), Equals, false) + c.Check(ch.VerbatimRiskOnly(), Equals, true) + c.Check(mustParse(c, "edge"), DeepEquals, ch.Clean()) + ch, err = channel.ParseVerbatim("latest/stable", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Track: "latest", Risk: "stable", }) + c.Check(ch.VerbatimTrackOnly(), Equals, false) + c.Check(ch.VerbatimRiskOnly(), Equals, false) c.Check(mustParse(c, "latest/stable"), DeepEquals, ch.Clean()) ch, err = channel.ParseVerbatim("latest/stable/foo", "") c.Assert(err, IsNil) c.Check(ch, DeepEquals, channel.Channel{ - Architecture: arch.UbuntuArchitecture(), + Architecture: arch.DpkgArchitecture(), Track: "latest", Risk: "stable", Branch: "foo", }) + c.Check(ch.VerbatimTrackOnly(), Equals, false) + c.Check(ch.VerbatimRiskOnly(), Equals, false) c.Check(mustParse(c, "latest/stable/foo"), DeepEquals, ch.Clean()) } @@ -266,3 +284,72 @@ func (s *storeChannelSuite) TestMatch(c *C) { c.Check(req.Match(&c1).String(), Equals, t.res) } } + +func (s *storeChannelSuite) TestResolve(c *C) { + tests := []struct { + channel string + new string + result string + expErr string + }{ + {"", "", "", ""}, + {"", "edge", "edge", ""}, + {"track/foo", "", "track/foo", ""}, + {"stable", "", "stable", ""}, + {"stable", "edge", "edge", ""}, + {"stable/branch1", "edge/branch2", "edge/branch2", ""}, + {"track", "track", "track", ""}, + {"track", "beta", "track/beta", ""}, + {"track/stable", "beta", "track/beta", ""}, + {"track/stable", "stable/branch", "track/stable/branch", ""}, + {"track/stable", "track/edge/branch", "track/edge/branch", ""}, + {"track/stable", "track/candidate", "track/candidate", ""}, + {"track/stable", "track/stable/branch", "track/stable/branch", ""}, + {"track1/stable", "track2/stable", "track2/stable", ""}, + {"track1/stable", "track2/stable/branch", "track2/stable/branch", ""}, + {"track/foo", "track/stable/branch", "", "invalid risk in channel name: track/foo"}, + } + + for _, t := range tests { + r, err := channel.Resolve(t.channel, t.new) + tcomm := Commentf("%#v", t) + if t.expErr == "" { + c.Assert(err, IsNil, tcomm) + c.Check(r, Equals, t.result, tcomm) + } else { + c.Assert(err, ErrorMatches, t.expErr, tcomm) + } + } +} + +func (s *storeChannelSuite) TestResolveLocked(c *C) { + tests := []struct { + track string + new string + result string + expErr string + }{ + {"", "", "", ""}, + {"", "anytrack/stable", "anytrack/stable", ""}, + {"track/foo", "", "", "invalid locked track: track/foo"}, + {"track", "", "track", ""}, + {"track", "track", "track", ""}, + {"track", "beta", "track/beta", ""}, + {"track", "stable/branch", "track/stable/branch", ""}, + {"track", "track/edge/branch", "track/edge/branch", ""}, + {"track", "track/candidate", "track/candidate", ""}, + {"track", "track/stable/branch", "track/stable/branch", ""}, + {"track1", "track2/stable", "track2/stable", "cannot switch locked track"}, + {"track1", "track2/stable/branch", "track2/stable/branch", "cannot switch locked track"}, + } + for _, t := range tests { + r, err := channel.ResolveLocked(t.track, t.new) + tcomm := Commentf("%#v", t) + if t.expErr == "" { + c.Assert(err, IsNil, tcomm) + c.Check(r, Equals, t.result, tcomm) + } else { + c.Assert(err, ErrorMatches, t.expErr, tcomm) + } + } +} diff --git a/snap/snapenv/snapenv.go b/snap/snapenv/snapenv.go index 58e47980b2..03ae4490bc 100644 --- a/snap/snapenv/snapenv.go +++ b/snap/snapenv/snapenv.go @@ -112,7 +112,7 @@ func basicEnv(info *snap.Info) map[string]string { "SNAP_INSTANCE_KEY": info.InstanceKey, "SNAP_VERSION": info.Version, "SNAP_REVISION": info.Revision.String(), - "SNAP_ARCH": arch.UbuntuArchitecture(), + "SNAP_ARCH": arch.DpkgArchitecture(), // see https://github.com/snapcore/snapd/pull/2732#pullrequestreview-18827193 "SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void", "SNAP_REEXEC": os.Getenv("SNAP_REEXEC"), diff --git a/snap/snapenv/snapenv_test.go b/snap/snapenv/snapenv_test.go index afa78fc5c0..030f35ede6 100644 --- a/snap/snapenv/snapenv_test.go +++ b/snap/snapenv/snapenv_test.go @@ -82,7 +82,7 @@ func (ts *HTestSuite) TestBasic(c *C) { c.Assert(env, DeepEquals, map[string]string{ "SNAP": fmt.Sprintf("%s/foo/17", dirs.CoreSnapMountDir), - "SNAP_ARCH": arch.UbuntuArchitecture(), + "SNAP_ARCH": arch.DpkgArchitecture(), "SNAP_COMMON": "/var/snap/foo/common", "SNAP_DATA": "/var/snap/foo/17", "SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void", @@ -136,7 +136,7 @@ func (s *HTestSuite) TestSnapRunSnapExecEnv(c *C) { env := snapEnv(info) c.Check(env, DeepEquals, map[string]string{ - "SNAP_ARCH": arch.UbuntuArchitecture(), + "SNAP_ARCH": arch.DpkgArchitecture(), "SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void", "SNAP_NAME": "snapname", "SNAP_INSTANCE_NAME": "snapname", @@ -178,7 +178,7 @@ func (s *HTestSuite) TestParallelInstallSnapRunSnapExecEnv(c *C) { env := snapEnv(info) c.Check(env, DeepEquals, map[string]string{ - "SNAP_ARCH": arch.UbuntuArchitecture(), + "SNAP_ARCH": arch.DpkgArchitecture(), "SNAP_LIBRARY_PATH": "/var/lib/snapd/lib/gl:/var/lib/snapd/lib/gl32:/var/lib/snapd/void", "SNAP_NAME": "snapname", "SNAP_INSTANCE_NAME": "snapname_foo", diff --git a/store/store.go b/store/store.go index 49ba063acc..ec359cb883 100644 --- a/store/store.go +++ b/store/store.go @@ -348,7 +348,7 @@ func New(cfg *Config, dauthCtx DeviceAndAuthContext) *Store { architecture := cfg.Architecture if cfg.Architecture == "" { - architecture = arch.UbuntuArchitecture() + architecture = arch.DpkgArchitecture() } series := cfg.Series diff --git a/store/store_test.go b/store/store_test.go index e963b30e23..ad482dbf4c 100644 --- a/store/store_test.go +++ b/store/store_test.go @@ -1997,7 +1997,7 @@ func (s *storeTestSuite) TestInfo(c *C) { query := r.URL.Query() c.Check(query.Get("fields"), Equals, "abc,def") - c.Check(query.Get("architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(query.Get("architecture"), Equals, arch.DpkgArchitecture()) w.Header().Set("X-Suggested-Currency", "GBP") w.WriteHeader(200) @@ -2961,7 +2961,7 @@ func (s *storeTestSuite) TestFind(c *C) { c.Check(r.URL.Query().Get("fields"), Equals, "abc,def") c.Check(r.Header.Get("X-Ubuntu-Series"), Equals, release.Series) - c.Check(r.Header.Get("X-Ubuntu-Architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(r.Header.Get("X-Ubuntu-Architecture"), Equals, arch.DpkgArchitecture()) c.Check(r.Header.Get("X-Ubuntu-Classic"), Equals, "false") c.Check(r.Header.Get("X-Ubuntu-Confinement"), Equals, "") @@ -4452,7 +4452,7 @@ func (s *storeTestSuite) TestSnapAction(c *C) { c.Check(storeID, Equals, "") c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series) - c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture()) c.Check(r.Header.Get("Snap-Classic"), Equals, "false") jsonReq, err := ioutil.ReadAll(r.Body) @@ -4562,7 +4562,7 @@ func (s *storeTestSuite) TestSnapActionNonZeroEpochAndEpochBump(c *C) { c.Check(storeID, Equals, "") c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series) - c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture()) c.Check(r.Header.Get("Snap-Classic"), Equals, "false") jsonReq, err := ioutil.ReadAll(r.Body) @@ -5567,7 +5567,7 @@ func (s *storeTestSuite) testSnapActionGet(action, cohort string, c *C) { c.Check(storeID, Equals, "") c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series) - c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture()) c.Check(r.Header.Get("Snap-Classic"), Equals, "false") jsonReq, err := ioutil.ReadAll(r.Body) @@ -5664,7 +5664,7 @@ func (s *storeTestSuite) TestSnapActionInstallAmend(c *C) { c.Check(storeID, Equals, "") c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series) - c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture()) c.Check(r.Header.Get("Snap-Classic"), Equals, "false") jsonReq, err := ioutil.ReadAll(r.Body) @@ -5834,7 +5834,7 @@ func (s *storeTestSuite) testSnapActionGetWithRevision(action string, c *C) { c.Check(storeID, Equals, "") c.Check(r.Header.Get("Snap-Device-Series"), Equals, release.Series) - c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.UbuntuArchitecture()) + c.Check(r.Header.Get("Snap-Device-Architecture"), Equals, arch.DpkgArchitecture()) c.Check(r.Header.Get("Snap-Classic"), Equals, "false") jsonReq, err := ioutil.ReadAll(r.Body) @@ -6564,7 +6564,7 @@ func (s *storeTestSuite) TestConnectivityCheckHappy(c *C) { switch r.URL.Path { case "/v2/snaps/info/core": c.Check(r.Method, Equals, "GET") - c.Check(r.URL.Query(), DeepEquals, url.Values{"fields": {"download"}, "architecture": {arch.UbuntuArchitecture()}}) + c.Check(r.URL.Query(), DeepEquals, url.Values{"fields": {"download"}, "architecture": {arch.DpkgArchitecture()}}) u, err := url.Parse("/download/core") c.Assert(err, IsNil) io.WriteString(w, diff --git a/systemd/systemd.go b/systemd/systemd.go index fdc7e0ed54..42f6466436 100644 --- a/systemd/systemd.go +++ b/systemd/systemd.go @@ -674,6 +674,7 @@ What=%s Where=%s Type=%s Options=%s +LazyUnmount=yes [Install] WantedBy=multi-user.target diff --git a/systemd/systemd_test.go b/systemd/systemd_test.go index cfdea50e8c..499aea317a 100644 --- a/systemd/systemd_test.go +++ b/systemd/systemd_test.go @@ -525,6 +525,7 @@ What=%s Where=/snap/snapname/123 Type=squashfs Options=nodev,ro,x-gdu.hide +LazyUnmount=yes [Install] WantedBy=multi-user.target @@ -557,6 +558,7 @@ What=%s Where=/snap/snapname/x1 Type=none Options=nodev,ro,x-gdu.hide,bind +LazyUnmount=yes [Install] WantedBy=multi-user.target @@ -595,6 +597,7 @@ What=%s Where=/snap/snapname/123 Type=squashfs Options=nodev,ro,x-gdu.hide,context=system_u:object_r:snappy_snap_t:s0 +LazyUnmount=yes [Install] WantedBy=multi-user.target @@ -637,6 +640,7 @@ What=%s Where=/snap/snapname/123 Type=fuse.squashfuse Options=nodev,ro,x-gdu.hide,allow_other +LazyUnmount=yes [Install] WantedBy=multi-user.target @@ -675,6 +679,7 @@ What=%s Where=/snap/snapname/123 Type=squashfs Options=nodev,ro,x-gdu.hide +LazyUnmount=yes [Install] WantedBy=multi-user.target diff --git a/tests/main/interfaces-calendar-service/task.yaml b/tests/main/interfaces-calendar-service/task.yaml index e79bf8a956..a445990001 100644 --- a/tests/main/interfaces-calendar-service/task.yaml +++ b/tests/main/interfaces-calendar-service/task.yaml @@ -5,8 +5,7 @@ summary: Ensure that the calendar-service interface works # # FIXME: disable opensuse-tumbleweed until # https://github.com/snapcore/snapd/pull/7230 is landed -# ubuntu-19.10-64: pending fix for this test coming on a following change -systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*, -opensuse-tumbleweed-*, -ubuntu-19.10-64] +systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*, -opensuse-tumbleweed-*] # fails in the autopkgtest env with: # [Wed Aug 15 16:34:12 2018] audit: type=1400 diff --git a/tests/main/interfaces-contacts-service/task.yaml b/tests/main/interfaces-contacts-service/task.yaml index 11e3af457c..2506da7a32 100644 --- a/tests/main/interfaces-contacts-service/task.yaml +++ b/tests/main/interfaces-contacts-service/task.yaml @@ -3,8 +3,7 @@ summary: Ensure that the contacts-service interface works # Only test on classic systems. Don't test on Ubuntu 14.04, which # does not ship a new enough evolution-data-server. # amazon: no need to run this on amazon -# ubuntu-19.10-64: pending fix for this test coming on a following change -systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*, -ubuntu-19.10-64] +systems: [-ubuntu-core-*, -ubuntu-14.04-*, -amazon-*, -centos-*] # fails in autopkgtest environment with: # [Wed Aug 15 16:08:23 2018] audit: type=1400 @@ -24,19 +23,15 @@ environment: debug: | echo "Output process to see what might write to $XDG" ps uafx + echo "Output dbus-session" + systemctl status dbus-session || true echo "Show what is in $XDG" ls -alR "$XDG" restore: | - if [ -e dbus-launch.pid ]; then - kill "$(cat dbus-launch.pid)" - fi - - # In case the process gvfsd-metadata does not finish by itself, it is manually stopped - # The reason is that gvfsd-metadata locks the xdg/share/gvfs-metadata directory content - # producing an error when the xdg directory is removed. - if pid="$(pidof gvfsd-metadata)"; then - kill -9 "$pid" || true + echo "Stop dbus session bus and all its children" + if systemctl is-active dbus-session; then + systemctl stop dbus-session fi rm -rf "$XDG" @@ -51,9 +46,10 @@ execute: | fi mkdir -p "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_CACHE_HOME" - echo "Setting up D-Bus session bus" - eval "$(dbus-launch --sh-syntax)" - echo "$DBUS_SESSION_BUS_PID" > dbus-launch.pid + echo "Setting up D-Bus session bus in a systemd unit" + systemd-run --unit=dbus-session --property=Type=forking -r /bin/sh -c "XDG_CONFIG_HOME=$XDG_CONFIG_HOME XDG_DATA_HOME=$XDG_DATA_HOME XDG_CACHE_HOME=$XDG_CACHE_HOME dbus-launch --sh-syntax > /tmp/dbus-sh" + retry-tool -n 20 test -e /tmp/dbus-sh + eval "$(cat /tmp/dbus-sh)" echo "The interface is initially disconnected" snap interfaces -i contacts-service | MATCH -- '- +test-snapd-eds:contacts-service' diff --git a/tests/main/interfaces-timeserver-control/task.yaml b/tests/main/interfaces-timeserver-control/task.yaml index 2d0a84be31..23828fdca3 100644 --- a/tests/main/interfaces-timeserver-control/task.yaml +++ b/tests/main/interfaces-timeserver-control/task.yaml @@ -7,8 +7,7 @@ details: | # Debian sid is skipped because "timedatectl set-ntp" fails with the error: # "Failed to set ntp: Message recipient disconnected from message bus without replying" # A workaround is to make "systemctl enable --now systemd-timesyncd.service" instead -# FIXME: re-enable ubuntu-19.10 after stabelizing it -systems: [-debian-sid-*, -ubuntu-19.10-*] +systems: [-debian-sid-*] prepare: | # shellcheck source=tests/lib/snaps.sh diff --git a/tests/unit/go/task.yaml b/tests/unit/go/task.yaml index 3ea8808ff1..95f1fb999f 100644 --- a/tests/unit/go/task.yaml +++ b/tests/unit/go/task.yaml @@ -1,8 +1,7 @@ summary: Run project static and unit tests # debian-sid complains about format-wrong most likely because of newer go -# ubuntu-19.10-64: pending fix for this test coming on a following change -systems: [-debian-sid-*, -ubuntu-19.10-64] +systems: [-debian-sid-*] # Start before anything else as it takes a long time. priority: 1000 |
