2024-02-01 09:29:54 +00:00
|
|
|
// -*- 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
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io/fs"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"regexp"
|
2024-05-29 16:58:22 +02:00
|
|
|
"strings"
|
2024-02-13 15:15:58 +00:00
|
|
|
"syscall"
|
2024-02-01 09:29:54 +00:00
|
|
|
|
|
|
|
|
"github.com/snapcore/snapd/dirs"
|
|
|
|
|
"github.com/snapcore/snapd/logger"
|
|
|
|
|
"github.com/snapcore/snapd/osutil"
|
|
|
|
|
"github.com/snapcore/snapd/snap"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// For testing purposes
|
|
|
|
|
var osSymlink = os.Symlink
|
|
|
|
|
|
|
|
|
|
// We expect as a minimum something that starts with three numbers
|
|
|
|
|
// separated by dots for the kernel version.
|
|
|
|
|
var utsRelease = regexp.MustCompile(`^([0-9]+\.){2}[0-9]+`)
|
|
|
|
|
|
|
|
|
|
// KernelVersionFromModulesDir returns the kernel version for a mounted kernel
|
|
|
|
|
// snap (this would be the output if "uname -r" for a running kernel). It
|
|
|
|
|
// assumes that there is a folder named modules/$(uname -r) inside the snap.
|
|
|
|
|
func KernelVersionFromModulesDir(mountPoint string) (string, error) {
|
|
|
|
|
modsDir := filepath.Join(mountPoint, "modules")
|
|
|
|
|
entries, err := os.ReadDir(modsDir)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
kversion := ""
|
|
|
|
|
for _, node := range entries {
|
|
|
|
|
if !node.Type().IsDir() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if !utsRelease.MatchString(node.Name()) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if kversion != "" {
|
|
|
|
|
return "", fmt.Errorf("more than one modules directory in %q", modsDir)
|
|
|
|
|
}
|
|
|
|
|
kversion = node.Name()
|
|
|
|
|
}
|
|
|
|
|
if kversion == "" {
|
|
|
|
|
return "", fmt.Errorf("no modules directory found in %q", modsDir)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return kversion, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
func createFirmwareSymlinks(fwMount MountPoints, fwDest string) error {
|
|
|
|
|
fwOrig := fwMount.UnderCurrentPath("firmware")
|
2024-02-01 09:29:54 +00:00
|
|
|
if err := os.MkdirAll(fwDest, 0755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Symbolic links inside firmware folder - it cannot be directly a
|
|
|
|
|
// symlink to "firmware" as we will use firmware/updates/ subfolder for
|
|
|
|
|
// components.
|
|
|
|
|
entries, err := os.ReadDir(fwOrig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
// Bit of a corner case, but maybe possible. Log anyway.
|
|
|
|
|
logger.Noticef("no firmware found in %q", fwOrig)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
fwTarget := fwMount.UnderTargetPath("firmware")
|
2024-02-01 09:29:54 +00:00
|
|
|
for _, node := range entries {
|
|
|
|
|
switch node.Type() {
|
|
|
|
|
case 0, fs.ModeDir:
|
2024-06-11 20:07:52 +01:00
|
|
|
// "updates" is included in (latest) kernel snaps but
|
|
|
|
|
// is empty, and we use if for firmware shipped in
|
|
|
|
|
// components, so we ignore it.
|
|
|
|
|
if node.Name() == "updates" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-02-01 09:29:54 +00:00
|
|
|
// Create link for regular files or directories
|
2024-06-11 20:07:52 +01:00
|
|
|
lpath := filepath.Join(fwDest, node.Name())
|
|
|
|
|
if err := os.Symlink(filepath.Join(fwTarget, node.Name()), lpath); err != nil {
|
2024-02-01 09:29:54 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
case fs.ModeSymlink:
|
|
|
|
|
// Replicate link (it should be relative)
|
|
|
|
|
// TODO check this in snap pack
|
|
|
|
|
lpath := filepath.Join(fwDest, node.Name())
|
2024-06-11 20:07:52 +01:00
|
|
|
dest, err := os.Readlink(filepath.Join(fwOrig, node.Name()))
|
2024-02-01 09:29:54 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if filepath.IsAbs(dest) {
|
|
|
|
|
return fmt.Errorf("symlink %q points to absolute path %q", lpath, dest)
|
|
|
|
|
}
|
|
|
|
|
if err := os.Symlink(dest, lpath); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("%q has unexpected file type: %s",
|
|
|
|
|
node.Name(), node.Type())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
func createModulesSubtree(kMntPts MountPoints, kernelTree, kversion string, compsMntPts []ModulesCompMountPoints) error {
|
2024-02-01 09:29:54 +00:00
|
|
|
// Although empty we need "lib" because "depmod" always appends
|
|
|
|
|
// "/lib/modules/<kernel_version>" to the directory passed with option
|
|
|
|
|
// "-b".
|
2024-02-13 15:15:58 +00:00
|
|
|
modsRoot := filepath.Join(kernelTree, "lib", "modules", kversion)
|
2024-02-01 09:29:54 +00:00
|
|
|
if err := os.MkdirAll(modsRoot, 0755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy modinfo files from the snap (these might be overwritten if
|
|
|
|
|
// kernel-modules components are installed).
|
2024-06-11 20:07:52 +01:00
|
|
|
modsGlob := kMntPts.UnderCurrentPath("modules", kversion, "modules.*")
|
2024-02-01 09:29:54 +00:00
|
|
|
modFiles, err := filepath.Glob(modsGlob)
|
|
|
|
|
if err != nil {
|
|
|
|
|
// Should not really happen (only possible error is ErrBadPattern)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, orig := range modFiles {
|
|
|
|
|
target := filepath.Join(modsRoot, filepath.Base(orig))
|
|
|
|
|
if err := osutil.CopyFile(orig, target, osutil.CopyFlagDefault); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
// Symbolic links to current mount of the kernel snap
|
|
|
|
|
currentMntDir := kMntPts.UnderCurrentPath("modules", kversion)
|
|
|
|
|
if err := createKernelModulesSymlinks(modsRoot, currentMntDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If necessary, add modules from components and run depmod
|
|
|
|
|
if err := setupModsFromComp(kernelTree, kversion, compsMntPts); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Change symlinks to target ones when needed
|
|
|
|
|
if !kMntPts.CurrentEqualsTarget() {
|
|
|
|
|
targetMntDir := kMntPts.UnderTargetPath("modules", kversion)
|
|
|
|
|
if err := createKernelModulesSymlinks(modsRoot, targetMntDir); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func createKernelModulesSymlinks(modsRoot, kMntPt string) error {
|
2024-02-01 09:29:54 +00:00
|
|
|
for _, d := range []string{"kernel", "vdso"} {
|
|
|
|
|
lname := filepath.Join(modsRoot, d)
|
2024-06-11 20:07:52 +01:00
|
|
|
to := filepath.Join(kMntPt, d)
|
|
|
|
|
// We might be re-creating, first remove
|
|
|
|
|
os.Remove(lname)
|
2024-02-01 09:29:54 +00:00
|
|
|
if err := osSymlink(to, lname); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
return nil
|
2024-02-13 15:15:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
func setupModsFromComp(kernelTree, kversion string, compsMntPts []ModulesCompMountPoints) error {
|
2024-02-13 15:15:58 +00:00
|
|
|
// This folder needs to exist always to allow for directory swapping
|
|
|
|
|
// in the future, even if right now we don't have components.
|
|
|
|
|
compsRoot := filepath.Join(kernelTree, "lib", "modules", kversion, "updates")
|
|
|
|
|
if err := os.MkdirAll(compsRoot, 0755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
if len(compsMntPts) == 0 {
|
2024-02-13 15:15:58 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Symbolic links to components
|
2024-06-11 20:07:52 +01:00
|
|
|
for _, cmp := range compsMntPts {
|
|
|
|
|
lname := filepath.Join(compsRoot, cmp.Name)
|
|
|
|
|
to := cmp.UnderCurrentPath("modules", kversion)
|
2024-02-13 15:15:58 +00:00
|
|
|
if err := osSymlink(to, lname); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run depmod
|
|
|
|
|
stdout, stderr, err := osutil.RunSplitOutput("depmod", "-b", kernelTree, kversion)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return osutil.OutputErrCombine(stdout, stderr, err)
|
|
|
|
|
}
|
|
|
|
|
logger.Noticef("depmod output:\n%s\n", string(osutil.CombineStdOutErr(stdout, stderr)))
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
// Change symlinks to target ones when needed
|
|
|
|
|
for _, cmp := range compsMntPts {
|
|
|
|
|
if cmp.CurrentEqualsTarget() {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
lname := filepath.Join(compsRoot, cmp.Name)
|
|
|
|
|
to := cmp.UnderTargetPath("modules", kversion)
|
|
|
|
|
// remove old link
|
|
|
|
|
os.Remove(lname)
|
|
|
|
|
if err := osSymlink(to, lname); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-01 09:29:54 +00:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 16:58:22 +02:00
|
|
|
// DriversTreeDir returns the directory for a given kernel and revision under
|
|
|
|
|
// rootdir.
|
|
|
|
|
func DriversTreeDir(rootdir, kernelName string, rev snap.Revision) string {
|
|
|
|
|
return filepath.Join(dirs.SnapKernelDriversTreesDirUnder(rootdir),
|
|
|
|
|
kernelName, rev.String())
|
2024-02-01 09:29:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-13 15:15:58 +00:00
|
|
|
// RemoveKernelDriversTree cleans-up the writable kernel tree in snapd data
|
|
|
|
|
// folder, under kernelSubdir/<rev> (kernelSubdir is usually the snap name).
|
2024-05-29 16:58:22 +02:00
|
|
|
// When called from the kernel package <rev> might be <rev>_tmp.
|
|
|
|
|
func RemoveKernelDriversTree(treeRoot string) (err error) {
|
2024-02-01 09:29:54 +00:00
|
|
|
return os.RemoveAll(treeRoot)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-13 15:15:58 +00:00
|
|
|
type KernelDriversTreeOptions struct {
|
|
|
|
|
// Set if we are building the tree for a kernel we are installing right now
|
|
|
|
|
KernelInstall bool
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 20:07:52 +01:00
|
|
|
// MountPoints describes mount points for a snap or a component.
|
|
|
|
|
type MountPoints struct {
|
|
|
|
|
// Current is where the container to be installed is currently
|
|
|
|
|
// available
|
|
|
|
|
Current string
|
|
|
|
|
// Target is where the container will be found in a running system
|
|
|
|
|
Target string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (mp *MountPoints) UnderCurrentPath(dirs ...string) string {
|
|
|
|
|
return filepath.Join(append([]string{mp.Current}, dirs...)...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (mp *MountPoints) UnderTargetPath(dirs ...string) string {
|
|
|
|
|
return filepath.Join(append([]string{mp.Target}, dirs...)...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (mp *MountPoints) CurrentEqualsTarget() bool {
|
|
|
|
|
return mp.Current == mp.Target
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ModulesCompMountPoints contains mount points for a component plus its name.
|
|
|
|
|
type ModulesCompMountPoints struct {
|
|
|
|
|
Name string
|
|
|
|
|
MountPoints
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-13 15:15:58 +00:00
|
|
|
// EnsureKernelDriversTree creates a drivers tree that can include modules/fw
|
|
|
|
|
// from kernel-modules components. opts.KernelInstall tells the function if
|
|
|
|
|
// this is a kernel install (which might be installing components at the same
|
|
|
|
|
// time) or an only components install.
|
|
|
|
|
//
|
2024-05-29 16:58:22 +02:00
|
|
|
// For kernel installs, this function creates a tree in destDir (should be of
|
|
|
|
|
// the form <somedir>/var/lib/snapd/kernel/<ksnapName>/<rev>), which is
|
|
|
|
|
// bind-mounted after a reboot to /usr/lib/{modules,firmware} (the currently
|
|
|
|
|
// active kernel is using a different path as it has a different revision).
|
|
|
|
|
// This tree contains files from the kernel snap content in kSnapRoot, as well
|
|
|
|
|
// as symlinks to it. Information from modules is found by looking at
|
2024-06-11 20:07:52 +01:00
|
|
|
// comps slice.
|
2024-02-13 15:15:58 +00:00
|
|
|
//
|
|
|
|
|
// For components-only install, we want the components to be available without
|
|
|
|
|
// rebooting. For this, we work on a temporary tree, and after finishing it we
|
|
|
|
|
// swap atomically the affected modules/firmware folders with those of the
|
|
|
|
|
// currently active kernel drivers tree.
|
2024-05-29 16:58:22 +02:00
|
|
|
//
|
2024-06-11 20:07:52 +01:00
|
|
|
// To make this work in all cases we need to know the current mounts of the
|
|
|
|
|
// kernel snap / components to be installed and the final mounts when the
|
|
|
|
|
// system is run after installation (as the installing system might be classic
|
|
|
|
|
// while the installed system could be hybrid or UC, or we could be installing
|
|
|
|
|
// from the initramfs). To consider all cases, we need to run depmod with links
|
|
|
|
|
// to the currently available content, and then replace those links with the
|
|
|
|
|
// expected mounts in the running system.
|
|
|
|
|
func EnsureKernelDriversTree(kMntPts MountPoints, compsMntPts []ModulesCompMountPoints, destDir string, opts *KernelDriversTreeOptions) (err error) {
|
2024-02-13 15:15:58 +00:00
|
|
|
// The temporal dir when installing only components can be fixed as a
|
|
|
|
|
// task installing/updating a kernel-modules component must conflict
|
|
|
|
|
// with changes containing this same task. This helps with clean-ups if
|
|
|
|
|
// something goes wrong. Note that this folder needs to be in the same
|
|
|
|
|
// filesystem as the final one so we can atomically switch the folders.
|
2024-05-29 16:58:22 +02:00
|
|
|
destDir = strings.TrimSuffix(destDir, "/")
|
|
|
|
|
targetDir := destDir + "_tmp"
|
2024-02-13 15:15:58 +00:00
|
|
|
if opts.KernelInstall {
|
2024-05-29 16:58:22 +02:00
|
|
|
targetDir = destDir
|
|
|
|
|
exists, isDir, _ := osutil.DirExists(targetDir)
|
|
|
|
|
if exists && isDir {
|
|
|
|
|
logger.Debugf("device tree %q already created on installation, not re-creating",
|
|
|
|
|
targetDir)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-02-13 15:15:58 +00:00
|
|
|
}
|
2024-02-01 09:29:54 +00:00
|
|
|
// Initial clean-up to make the function idempotent
|
2024-05-29 16:58:22 +02:00
|
|
|
if rmErr := RemoveKernelDriversTree(targetDir); rmErr != nil &&
|
2024-02-01 09:29:54 +00:00
|
|
|
!errors.Is(err, fs.ErrNotExist) {
|
|
|
|
|
logger.Noticef("while removing old kernel tree: %v", rmErr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defer func() {
|
2024-02-13 15:15:58 +00:00
|
|
|
// Remove on return if error or if temporary tree
|
|
|
|
|
if err == nil && opts.KernelInstall {
|
|
|
|
|
return
|
|
|
|
|
}
|
2024-05-29 16:58:22 +02:00
|
|
|
if rmErr := RemoveKernelDriversTree(targetDir); rmErr != nil &&
|
2024-02-13 15:15:58 +00:00
|
|
|
!errors.Is(err, fs.ErrNotExist) {
|
|
|
|
|
logger.Noticef("while cleaning up kernel tree: %v", rmErr)
|
2024-02-01 09:29:54 +00:00
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
|
2024-02-13 15:15:58 +00:00
|
|
|
// Create drivers tree
|
2024-06-11 20:07:52 +01:00
|
|
|
kversion, err := KernelVersionFromModulesDir(kMntPts.Current)
|
2024-02-13 15:15:58 +00:00
|
|
|
if err == nil {
|
2024-06-11 20:07:52 +01:00
|
|
|
if err := createModulesSubtree(kMntPts, targetDir,
|
|
|
|
|
kversion, compsMntPts); err != nil {
|
2024-02-13 15:15:58 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Bit of a corner case, but maybe possible. Log anyway.
|
|
|
|
|
// TODO detect this issue in snap pack, should be enforced
|
|
|
|
|
// if the snap declares kernel-modules components.
|
2024-06-11 20:07:52 +01:00
|
|
|
logger.Noticef("no modules found in %q", kMntPts.Current)
|
2024-02-01 09:29:54 +00:00
|
|
|
}
|
|
|
|
|
|
2024-05-29 16:58:22 +02:00
|
|
|
fwDir := filepath.Join(targetDir, "lib", "firmware")
|
2024-02-13 15:15:58 +00:00
|
|
|
if opts.KernelInstall {
|
|
|
|
|
// symlinks in /lib/firmware are not affected by components
|
2024-06-11 20:07:52 +01:00
|
|
|
if err := createFirmwareSymlinks(kMntPts, fwDir); err != nil {
|
2024-02-13 15:15:58 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
updateFwDir := filepath.Join(fwDir, "updates")
|
|
|
|
|
// This folder needs to exist always to allow for directory swapping
|
|
|
|
|
// in the future, even if right now we don't have components.
|
|
|
|
|
if err := os.MkdirAll(updateFwDir, 0755); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-06-11 20:07:52 +01:00
|
|
|
for _, cmp := range compsMntPts {
|
|
|
|
|
if err := createFirmwareSymlinks(cmp.MountPoints, updateFwDir); err != nil {
|
2024-02-13 15:15:58 +00:00
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sync before returning successfully (install kernel case) and also
|
|
|
|
|
// for swapping case so we have consistent content before swapping
|
|
|
|
|
// folder.
|
|
|
|
|
syscall.Sync()
|
|
|
|
|
|
|
|
|
|
if !opts.KernelInstall {
|
|
|
|
|
// There is a (very small) chance of a poweroff/reboot while
|
|
|
|
|
// having swapped only one of these two folders. If that
|
|
|
|
|
// happens, snapd will re-run the task on the next boot, but
|
|
|
|
|
// with mismatching modules/fw for the installed components. As
|
|
|
|
|
// modules shipped by components should not be that critical,
|
|
|
|
|
// in principle the system should recover.
|
|
|
|
|
|
|
|
|
|
// Swap modules directories
|
2024-05-29 16:58:22 +02:00
|
|
|
oldRoot := destDir
|
2024-02-13 15:15:58 +00:00
|
|
|
|
|
|
|
|
// Swap updates directory inside firmware dir
|
|
|
|
|
oldFwUpdates := filepath.Join(oldRoot, "lib", "firmware", "updates")
|
|
|
|
|
if err := osutil.SwapDirs(oldFwUpdates, updateFwDir); err != nil {
|
|
|
|
|
return fmt.Errorf("while swapping %q <-> %q: %w", oldFwUpdates, updateFwDir, err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-29 16:58:22 +02:00
|
|
|
newMods := filepath.Join(targetDir, "lib", "modules", kversion)
|
2024-02-13 15:15:58 +00:00
|
|
|
oldMods := filepath.Join(oldRoot, "lib", "modules", kversion)
|
|
|
|
|
if err := osutil.SwapDirs(oldMods, newMods); err != nil {
|
|
|
|
|
// Undo firmware swap
|
|
|
|
|
if err := osutil.SwapDirs(oldFwUpdates, updateFwDir); err != nil {
|
|
|
|
|
logger.Noticef("while reverting modules swap: %v", err)
|
|
|
|
|
}
|
|
|
|
|
return fmt.Errorf("while swapping %q <-> %q: %w", newMods, oldMods, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make sure that changes are written
|
|
|
|
|
syscall.Sync()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
2024-02-01 09:29:54 +00:00
|
|
|
}
|