Files
snapd/kernel/kernel_drivers_test.go
alfonsosanchezbeato 20cde6b251 many: build drivers tree when current mount is not the target mount (#14047)
* tests: fix muinstaller tests for 24

* many: build drivers tree when current mount is not the target mount

In some cases (when using the snapd install API or when installing
from initramfs), the place where the kernel snap / components used for the
installation are mounted is different to the final location in the
installed system. This change considers this so the drivers tree is
generated with symlinks pointing to the final expected location.

* overlord: use model to check if we need to set-up drivers tree

instead of using a device context, as for the installation using snapd
API case we have a model but not a context.

* tests/lib/tools/setup_nested_hybrid_system.sh: re-try kpartx -d

* tests/muinstaller-real: check that drivers tree is created

* tests/muinstaller-real: we need a bigger disk with latest kernel

* tests/lib/tools/setup_nested_hybrid_system.sh: clean up

after building muinstaller. On classic we have weird issues otherwise
due to a desktop agent installing lxd.

* tests/lib/prepare-restore.sh: purge lxd-installer

lxd-installer was causing failures in the restore step for 24.04.
2024-06-11 20:07:52 +01:00

682 lines
25 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2024 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package kernel_test
import (
"errors"
"io/fs"
"os"
"path/filepath"
"syscall"
. "gopkg.in/check.v1"
"github.com/snapcore/snapd/dirs"
"github.com/snapcore/snapd/kernel"
"github.com/snapcore/snapd/logger"
"github.com/snapcore/snapd/osutil"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/testutil"
)
type kernelDriversTestSuite struct {
testutil.BaseTest
}
var _ = Suite(&kernelDriversTestSuite{})
func (s *kernelDriversTestSuite) SetUpTest(c *C) {
s.BaseTest.SetUpTest(c)
dirs.SetRootDir(c.MkDir())
s.AddCleanup(func() { dirs.SetRootDir("") })
}
func (s *kernelDriversTestSuite) TestKernelVersionFromModulesDir(c *C) {
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
c.Assert(os.MkdirAll(mountDir, 0755), IsNil)
// No map file
ver, err := kernel.KernelVersionFromModulesDir(mountDir)
c.Check(err, ErrorMatches, `open .*/run/mnt/pc-kernel/modules: no such file or directory`)
c.Check(ver, Equals, "")
// Create directory so kernel version can be found
c.Assert(os.MkdirAll(filepath.Join(
mountDir, "modules", "5.15.0-78-generic"), 0755), IsNil)
ver, err = kernel.KernelVersionFromModulesDir(mountDir)
c.Check(err, IsNil)
c.Check(ver, Equals, "5.15.0-78-generic")
// Too many matches
c.Assert(os.MkdirAll(filepath.Join(
mountDir, "modules", "5.15.0-90-generic"), 0755), IsNil)
ver, err = kernel.KernelVersionFromModulesDir(mountDir)
c.Check(err, ErrorMatches, `more than one modules directory in ".*/run/mnt/pc-kernel/modules"`)
c.Check(ver, Equals, "")
}
func (s *kernelDriversTestSuite) TestKernelVersionFromModulesDirNoModDir(c *C) {
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
c.Assert(os.MkdirAll(mountDir, 0755), IsNil)
c.Assert(os.MkdirAll(filepath.Join(mountDir, "modules"), 0755), IsNil)
// Create file instead of directory
c.Assert(os.WriteFile(filepath.Join(
mountDir, "modules", "5.15.0-78-generic"), []byte{}, 0644), IsNil)
ver, err := kernel.KernelVersionFromModulesDir(mountDir)
c.Check(err, ErrorMatches, `no modules directory found in ".*/run/mnt/pc-kernel/modules"`)
c.Check(ver, Equals, "")
}
func (s *kernelDriversTestSuite) TestKernelVersionFromModulesDirBadVersion(c *C) {
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
c.Assert(os.MkdirAll(filepath.Join(
mountDir, "modules", "5.15.myway"), 0755), IsNil)
ver, err := kernel.KernelVersionFromModulesDir(mountDir)
c.Check(err, ErrorMatches, `no modules directory found in ".*/run/mnt/pc-kernel/modules"`)
c.Check(ver, Equals, "")
}
type createKernelSnapFilesOpts struct {
withFwUpdatesDir bool
}
func createKernelSnapFiles(c *C, kversion, kdir string, opts createKernelSnapFilesOpts) {
c.Assert(os.MkdirAll(kdir, 0755), IsNil)
// Create modinfo files
modDir := filepath.Join(kdir, "modules", kversion)
c.Assert(os.MkdirAll(modDir, 0755), IsNil)
modFile := []string{"modules.builtin.alias.bin", "modules.dep.bin", "modules.symbols"}
allFiles := append(modFile, "other.mod", "foo.bin")
for _, f := range allFiles {
c.Assert(os.WriteFile(filepath.Join(modDir, f), []byte{}, 0644), IsNil)
}
// Create firmware
fwDir := filepath.Join(kdir, "firmware")
c.Assert(os.MkdirAll(fwDir, 0755), IsNil)
// Regular files
for _, f := range []string{"blob1", "blob2"} {
c.Assert(os.WriteFile(filepath.Join(fwDir, f), []byte{}, 0644), IsNil)
}
if opts.withFwUpdatesDir {
c.Assert(os.MkdirAll(filepath.Join(fwDir, "updates"), 0755), IsNil)
}
// Directory, write file inside
fwSubDir := filepath.Join(fwDir, "subdir")
c.Assert(os.MkdirAll(fwSubDir, 0755), IsNil)
blob3 := filepath.Join(fwSubDir, "blob3")
c.Assert(os.WriteFile(blob3, []byte{}, 0644), IsNil)
// Symlink
os.Symlink("subdir/blob3", filepath.Join(fwDir, "ln_to_blob3"))
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTree(c *C) {
// Build twice to make sure the function is idempotent
testBuildKernelDriversTree(c, createKernelSnapFilesOpts{})
testBuildKernelDriversTree(c, createKernelSnapFilesOpts{})
// Now remove and check
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
kernel.RemoveKernelDriversTree(treeRoot)
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTreeWithUpdates(c *C) {
testBuildKernelDriversTree(c, createKernelSnapFilesOpts{withFwUpdatesDir: true})
// Now remove and check
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
kernel.RemoveKernelDriversTree(treeRoot)
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
type expectInode struct {
file string
fType fs.FileMode
linkTarget string
}
func doDirChecks(c *C, dir string, expected []expectInode) {
entries, err := os.ReadDir(dir)
c.Assert(err, IsNil)
c.Assert(len(entries), Equals, len(expected))
for i, ent := range entries {
c.Check(ent.Name(), Equals, expected[i].file)
c.Check(ent.Type(), Equals, expected[i].fType)
if ent.Type() == fs.ModeSymlink {
dest, err := os.Readlink(filepath.Join(dir, ent.Name()))
c.Assert(err, IsNil)
c.Check(dest, Equals, expected[i].linkTarget)
}
}
}
func testBuildKernelDriversTree(c *C, opts createKernelSnapFilesOpts) {
mountDir := filepath.Join(dirs.SnapMountDir, "pc-kernel/1")
kversion := "5.15.0-78-generic"
createKernelSnapFiles(c, kversion, mountDir, opts)
// Now build the tree
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
c.Assert(kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir},
nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true}), IsNil)
// Check content is as expected
modsRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "modules", kversion)
modsMntDir := filepath.Join(mountDir, "modules", kversion)
expected := []expectInode{
{"kernel", fs.ModeSymlink, filepath.Join(modsMntDir, "kernel")},
{"modules.builtin.alias.bin", 0, ""},
{"modules.dep.bin", 0, ""},
{"modules.symbols", 0, ""},
{"updates", fs.ModeDir, ""},
{"vdso", fs.ModeSymlink, filepath.Join(modsMntDir, "vdso")},
}
doDirChecks(c, modsRoot, expected)
// Check firmware entries
fwRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "firmware")
fwMntDir := filepath.Join(mountDir, "firmware")
expected = []expectInode{
{"blob1", fs.ModeSymlink, filepath.Join(fwMntDir, "blob1")},
{"blob2", fs.ModeSymlink, filepath.Join(fwMntDir, "blob2")},
{"ln_to_blob3", fs.ModeSymlink, "subdir/blob3"},
{"subdir", fs.ModeSymlink, filepath.Join(fwMntDir, "subdir")},
{"updates", fs.ModeDir, ""},
}
doDirChecks(c, fwRoot, expected)
// Check symlinks to files point to real files
for _, ln := range []string{
filepath.Join(fwRoot, "blob1"),
filepath.Join(fwRoot, "blob2"),
filepath.Join(fwRoot, "ln_to_blob3"),
filepath.Join(fwRoot, "subdir/blob3"),
} {
path, err := filepath.EvalSymlinks(ln)
c.Assert(err, IsNil)
exists, isReg, err := osutil.RegularFileExists(path)
c.Assert(err, IsNil)
c.Check(exists, Equals, true)
c.Check(isReg, Equals, true)
}
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversNoModsOrFw(c *C) {
buf, restore := logger.MockLogger()
defer restore()
mountDir := filepath.Join(dirs.SnapMountDir, "pc-kernel/11")
c.Assert(os.MkdirAll(mountDir, 0755), IsNil)
// Build the tree should not fail
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, IsNil)
// but log should warn about this
c.Assert(buf.String(), testutil.Contains, `no modules found in "`+mountDir+`"`)
c.Assert(buf.String(), testutil.Contains, `no firmware found in "`+mountDir+`/firmware"`)
}
func createKernelSnapFilesOnlyModules(c *C, kversion, kdir string) {
c.Assert(os.MkdirAll(kdir, 0755), IsNil)
// Create modinfo files
modDir := filepath.Join(kdir, "modules", kversion)
c.Assert(os.MkdirAll(modDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(modDir, "modules.dep.bin"), []byte{}, 0644), IsNil)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversOnlyMods(c *C) {
buf, restore := logger.MockLogger()
defer restore()
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
kversion := "5.15.0-78-generic"
createKernelSnapFilesOnlyModules(c, kversion, mountDir)
// Build the tree should not fail
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, IsNil)
// check created file
modPath := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "modules", kversion, "modules.dep.bin")
exists, isReg, err := osutil.RegularFileExists(modPath)
c.Assert(err, IsNil)
c.Check(exists, Equals, true)
c.Check(isReg, Equals, true)
// but log should warn about this
c.Assert(buf.String(), testutil.Contains, `no firmware found in "`+mountDir+`/firmware"`)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversOnlyModsWithTargetDir(c *C) {
buf, restore := logger.MockLogger()
defer restore()
mountDir := filepath.Join(dirs.RunDir, "mnt/tmp-mount")
kTargetDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
kversion := "5.15.0-78-generic"
createKernelSnapFilesOnlyModules(c, kversion, mountDir)
// Build the tree should not fail
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: kTargetDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, IsNil)
// check created file
modPath := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "modules", kversion)
modDepBinPath := filepath.Join(modPath, "modules.dep.bin")
exists, isReg, err := osutil.RegularFileExists(modDepBinPath)
c.Assert(err, IsNil)
c.Check(exists, Equals, true)
c.Check(isReg, Equals, true)
// Check symlinks points to final target
modsPath := filepath.Join(modPath, "kernel")
modsTarget, err := os.Readlink(modsPath)
c.Assert(err, IsNil)
c.Check(modsTarget, Equals, filepath.Join(kTargetDir, "modules", kversion, "kernel"))
// but log should warn about this
c.Assert(buf.String(), testutil.Contains, `no firmware found in "`+mountDir+`/firmware"`)
}
func createKernelSnapFilesOnlyFw(c *C, kdir string) {
c.Assert(os.MkdirAll(kdir, 0755), IsNil)
// Create firmware files
fwDir := filepath.Join(kdir, "firmware")
c.Assert(os.MkdirAll(fwDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(fwDir, "wifi_fw.bin"), []byte{}, 0644), IsNil)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversOnlyFw(c *C) {
buf, restore := logger.MockLogger()
defer restore()
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
createKernelSnapFilesOnlyFw(c, mountDir)
// Build the tree should not fail
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, IsNil)
// check link
fwPath := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "firmware", "wifi_fw.bin")
c.Assert(osutil.IsSymlink(fwPath), Equals, true)
// but log should warn about this
c.Assert(buf.String(), testutil.Contains, `no modules found in "`+mountDir+`"`)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversOnlyFwWithTargetDir(c *C) {
buf, restore := logger.MockLogger()
defer restore()
mountDir := filepath.Join(dirs.RunDir, "mnt/tmp-mount")
kTargetDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
createKernelSnapFilesOnlyFw(c, mountDir)
// Build the tree should not fail
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: kTargetDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, IsNil)
// check link
fwPath := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "firmware", "wifi_fw.bin")
fwPathTarget, err := os.Readlink(fwPath)
c.Assert(err, IsNil)
c.Check(fwPathTarget, Equals, filepath.Join(kTargetDir, "firmware", "wifi_fw.bin"))
// but log should warn about this
c.Assert(buf.String(), testutil.Contains, `no modules found in "`+mountDir+`"`)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversAbsFwSymlink(c *C) {
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
// Create firmware files
fwDir := filepath.Join(mountDir, "firmware")
c.Assert(os.MkdirAll(fwDir, 0755), IsNil)
// Symlink
os.Symlink("/absdir/blob3", filepath.Join(fwDir, "ln_to_abs"))
// Fails on the absolute path in the link
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, ErrorMatches, `symlink \".*lib/firmware/ln_to_abs\" points to absolute path \"/absdir/blob3\"`)
// Make sure the tree has been deleted
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTreeCleanup(c *C) {
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
kversion := "5.15.0-78-generic"
createKernelSnapFiles(c, kversion, mountDir, createKernelSnapFilesOpts{})
restore := kernel.MockOsSymlink(func(string, string) error {
return errors.New("mocked symlink error")
})
defer restore()
// Now build the tree
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, ErrorMatches, "mocked symlink error")
// Make sure the tree has been deleted
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversBadFileType(c *C) {
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
kversion := "5.15.0-78-generic"
createKernelSnapFiles(c, kversion, mountDir, createKernelSnapFilesOpts{})
// Additional file of not expected type in "firmware"
fwDir := filepath.Join(mountDir, "firmware")
c.Assert(syscall.Mkfifo(filepath.Join(fwDir, "fifo"), 0666), IsNil)
// Now build the tree
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir}, nil, destDir,
&kernel.KernelDriversTreeOptions{KernelInstall: true})
c.Assert(err, ErrorMatches, `"fifo" has unexpected file type: p---------`)
// Make sure the tree has been deleted
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
func createKernelModulesCompFiles(c *C, kversion, compdir, filePrefix string) {
c.Assert(os.MkdirAll(compdir, 0755), IsNil)
// Create some kernel module file
modDir := filepath.Join(compdir, "modules", kversion, "kernel/foo")
c.Assert(os.MkdirAll(modDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(modDir, filePrefix+".ko.zst"), []byte{}, 0644), IsNil)
// and some fw
fwDir := filepath.Join(compdir, "firmware")
c.Assert(os.MkdirAll(fwDir, 0755), IsNil)
c.Assert(os.WriteFile(filepath.Join(fwDir, filePrefix+".bin"), []byte{}, 0644), IsNil)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTreeWithKernelAndComps(c *C) {
// Build twice to make sure the function is idempotent
opts := &kernel.KernelDriversTreeOptions{KernelInstall: true}
testBuildKernelDriversTreeWithComps(c, opts)
testBuildKernelDriversTreeWithComps(c, opts)
// Now remove and check
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
kernel.RemoveKernelDriversTree(treeRoot)
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTreeCompsNoKernelInstall(c *C) {
// Kernel needs to have been installed first
testBuildKernelDriversTree(c, createKernelSnapFilesOpts{})
// Build twice to make sure the function is idempotent
opts := &kernel.KernelDriversTreeOptions{KernelInstall: false}
testBuildKernelDriversTreeWithComps(c, opts)
testBuildKernelDriversTreeWithComps(c, opts)
// Now remove and check
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1")
kernel.RemoveKernelDriversTree(treeRoot)
c.Assert(osutil.FileExists(treeRoot), Equals, false)
// No _tmp folder should be around
treeRoot = filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1_tmp")
c.Assert(osutil.FileExists(treeRoot), Equals, false)
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTreeCompsNoKernel(c *C) {
mockCmd := testutil.MockCommand(c, "depmod", "")
defer mockCmd.Restore()
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
kversion := "5.15.0-78-generic"
createKernelSnapFiles(c, kversion, mountDir, createKernelSnapFilesOpts{})
compMntDir1 := filepath.Join(dirs.RunDir, "mnt/kernel-snaps/comp1")
compMntDir2 := filepath.Join(dirs.RunDir, "mnt/kernel-snaps/comp2")
createKernelModulesCompFiles(c, kversion, compMntDir1, "comp1")
createKernelModulesCompFiles(c, kversion, compMntDir2, "comp2")
kmodsConts := []snap.ContainerPlaceInfo{
snap.MinimalComponentContainerPlaceInfo("comp1", snap.R(11), "pc-kernel"),
snap.MinimalComponentContainerPlaceInfo("comp2", snap.R(22), "pc-kernel"),
}
compsMntPts := []kernel.ModulesCompMountPoints{
{"comp1", kernel.MountPoints{kmodsConts[0].MountDir(), kmodsConts[0].MountDir()}},
{"comp2", kernel.MountPoints{kmodsConts[1].MountDir(), kmodsConts[1].MountDir()}},
}
// Now build the tree, will fail as no kernel was installed previously
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir},
compsMntPts, destDir, &kernel.KernelDriversTreeOptions{KernelInstall: false})
c.Assert(err, ErrorMatches, `while swapping .*: no such file or directory`)
}
func testBuildKernelDriversTreeWithComps(c *C, opts *kernel.KernelDriversTreeOptions) {
mockCmd := testutil.MockCommand(c, "depmod", "")
defer mockCmd.Restore()
mountDir := filepath.Join(dirs.SnapMountDir, "pc-kernel/1")
kversion := "5.15.0-78-generic"
createKernelSnapFiles(c, kversion, mountDir, createKernelSnapFilesOpts{})
compMntDir1 := filepath.Join(dirs.SnapMountDir, "pc-kernel/components/mnt/comp1/11")
compMntDir2 := filepath.Join(dirs.SnapMountDir, "pc-kernel/components/mnt/comp2/22")
createKernelModulesCompFiles(c, kversion, compMntDir1, "comp1")
createKernelModulesCompFiles(c, kversion, compMntDir2, "comp2")
kmodsConts := []snap.ContainerPlaceInfo{
snap.MinimalComponentContainerPlaceInfo("comp1", snap.R(11), "pc-kernel"),
snap.MinimalComponentContainerPlaceInfo("comp2", snap.R(22), "pc-kernel"),
}
compsMntPts := []kernel.ModulesCompMountPoints{
{"comp1", kernel.MountPoints{kmodsConts[0].MountDir(), kmodsConts[0].MountDir()}},
{"comp2", kernel.MountPoints{kmodsConts[1].MountDir(), kmodsConts[1].MountDir()}},
}
workSubdir := "1_tmp"
if opts.KernelInstall {
workSubdir = "1"
}
treeRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", workSubdir)
// Find out if the directory already exists, as in that case
// there are no calls to depmod
exists, isDir, err := osutil.DirExists(treeRoot)
c.Assert(err, IsNil)
// Now build the tree
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
c.Assert(kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir},
compsMntPts, destDir, opts), IsNil)
if exists {
c.Assert(isDir, Equals, true)
c.Assert(mockCmd.Calls(), IsNil)
} else {
c.Assert(mockCmd.Calls(), DeepEquals, [][]string{
{"depmod", "-b", treeRoot, kversion},
})
}
// Check modules root dir is as expected
modsRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "modules", kversion)
modsMntDir := filepath.Join(mountDir, "modules", kversion)
expected := []expectInode{
{"kernel", fs.ModeSymlink, filepath.Join(modsMntDir, "kernel")},
{"modules.builtin.alias.bin", 0, ""},
{"modules.dep.bin", 0, ""},
{"modules.symbols", 0, ""},
{"updates", fs.ModeDir, ""},
{"vdso", fs.ModeSymlink, filepath.Join(modsMntDir, "vdso")},
}
doDirChecks(c, modsRoot, expected)
// Check links for modules shipped in components
updatesDir := filepath.Join(modsRoot, "updates")
expected = []expectInode{
{"comp1", fs.ModeSymlink, filepath.Join(compMntDir1, "modules", kversion)},
{"comp2", fs.ModeSymlink, filepath.Join(compMntDir2, "modules", kversion)},
}
doDirChecks(c, updatesDir, expected)
// Check firmware entries from snap
fwRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "firmware")
fwMntDir := filepath.Join(mountDir, "firmware")
expected = []expectInode{
{"blob1", fs.ModeSymlink, filepath.Join(fwMntDir, "blob1")},
{"blob2", fs.ModeSymlink, filepath.Join(fwMntDir, "blob2")},
{"ln_to_blob3", fs.ModeSymlink, "subdir/blob3"},
{"subdir", fs.ModeSymlink, filepath.Join(fwMntDir, "subdir")},
{"updates", fs.ModeDir, ""},
}
doDirChecks(c, fwRoot, expected)
// Check firmware entries from components
fwUpdates := filepath.Join(fwRoot, "updates")
expected = []expectInode{
{"comp1.bin", fs.ModeSymlink, filepath.Join(compMntDir1, "firmware/comp1.bin")},
{"comp2.bin", fs.ModeSymlink, filepath.Join(compMntDir2, "firmware/comp2.bin")},
}
doDirChecks(c, fwUpdates, expected)
// Check symlinks to files point to real files
for _, ln := range []string{
filepath.Join(updatesDir, "comp1/kernel/foo/comp1.ko.zst"),
filepath.Join(updatesDir, "comp2/kernel/foo/comp2.ko.zst"),
filepath.Join(fwRoot, "blob1"),
filepath.Join(fwRoot, "blob2"),
filepath.Join(fwRoot, "ln_to_blob3"),
filepath.Join(fwRoot, "subdir/blob3"),
filepath.Join(fwUpdates, "comp1.bin"),
filepath.Join(fwUpdates, "comp2.bin"),
} {
path, err := filepath.EvalSymlinks(ln)
c.Assert(err, IsNil)
exists, isReg, err := osutil.RegularFileExists(path)
c.Assert(err, IsNil)
c.Check(exists, Equals, true)
c.Check(isReg, Equals, true)
}
if !opts.KernelInstall {
// Check that there is no tmp folder left behind
tmpDir := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1_tmp")
exists, _, _ = osutil.RegularFileExists(tmpDir)
c.Check(exists, Equals, false)
}
}
func (s *kernelDriversTestSuite) TestBuildKernelDriversTreeCompsWithTargetDir(c *C) {
mockCmd := testutil.MockCommand(c, "depmod", "")
defer mockCmd.Restore()
// Kernel needs to have been installed first
testBuildKernelDriversTree(c, createKernelSnapFilesOpts{})
mountDir := filepath.Join(dirs.RunDir, "mnt/pc-kernel")
kversion := "5.15.0-78-generic"
createKernelSnapFiles(c, kversion, mountDir, createKernelSnapFilesOpts{})
compMntDir1 := filepath.Join(dirs.RunDir, "mnt/kernel-snaps/comp1")
createKernelModulesCompFiles(c, kversion, compMntDir1, "comp1")
kmodCont := snap.MinimalComponentContainerPlaceInfo("comp1", snap.R(11), "pc-kernel")
// Current mount is different to the one in the final system
compsMntPts := []kernel.ModulesCompMountPoints{
{"comp1", kernel.MountPoints{
Current: compMntDir1,
Target: kmodCont.MountDir()}},
}
// Now build the tree, will fail as no kernel was installed previously
destDir := kernel.DriversTreeDir(dirs.GlobalRootDir, "pc-kernel", snap.R(1))
err := kernel.EnsureKernelDriversTree(
kernel.MountPoints{
Current: mountDir,
Target: mountDir},
compsMntPts, destDir, &kernel.KernelDriversTreeOptions{KernelInstall: false})
c.Assert(err, IsNil)
// Check firmware entries from components
fwRoot := filepath.Join(dirs.SnapdStateDir(dirs.GlobalRootDir), "kernel", "pc-kernel", "1", "lib", "firmware")
fwUpdates := filepath.Join(fwRoot, "updates")
expected := []expectInode{
{"comp1.bin", fs.ModeSymlink, filepath.Join(kmodCont.MountDir(), "firmware/comp1.bin")},
}
doDirChecks(c, fwUpdates, expected)
}