mirror of
https://github.com/siderolabs/talos.git
synced 2025-10-10 15:11:15 +02:00
This fixes the issue when the overlay mount target directory was used as lowerdir for the mount, creating extra folders in the extension. Fix the issue by adding support for normal overlay mounts to use a source directory when specified. Also fixes a small issue where messages was logged when error is nil. Signed-off-by: Noel Georgi <git@frezbo.dev>
243 lines
6.5 KiB
Go
243 lines
6.5 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
// Package extensions provides function to manage system extensions.
|
|
package extensions
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/freddierice/go-losetup/v2"
|
|
"github.com/u-root/u-root/pkg/cpio"
|
|
"github.com/ulikunitz/xz"
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/siderolabs/talos/internal/pkg/mount"
|
|
"github.com/siderolabs/talos/pkg/machinery/constants"
|
|
"github.com/siderolabs/talos/pkg/machinery/extensions"
|
|
)
|
|
|
|
// ProvidesKernelModules returns true if the extension provides kernel modules.
|
|
func (ext *Extension) ProvidesKernelModules() bool {
|
|
if _, err := os.Stat(filepath.Join(ext.rootfsPath, constants.DefaultKernelModulesPath)); os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// KernelModuleDirectory returns the path to the kernel modules directory.
|
|
func (ext *Extension) KernelModuleDirectory() string {
|
|
return filepath.Join(ext.rootfsPath, constants.DefaultKernelModulesPath)
|
|
}
|
|
|
|
// GenerateKernelModuleDependencyTreeExtension generates a kernel module dependency tree extension.
|
|
// nolint:gocyclo
|
|
func GenerateKernelModuleDependencyTreeExtension(extensionsPathWithKernelModules []string, arch string) (*Extension, error) {
|
|
log.Println("preparing to run depmod to generate kernel modules dependency tree")
|
|
|
|
tempDir, err := os.MkdirTemp("", "ext-modules")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer logErr(func() error {
|
|
return os.RemoveAll(tempDir)
|
|
})
|
|
|
|
initramfsxz, err := os.Open(fmt.Sprintf(constants.InitramfsAssetPath, arch))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer logErr(func() error {
|
|
return initramfsxz.Close()
|
|
})
|
|
|
|
r, err := xz.NewReader(initramfsxz)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var buff bytes.Buffer
|
|
|
|
if _, err = io.Copy(&buff, r); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tempRootfsFile := filepath.Join(tempDir, constants.RootfsAsset)
|
|
|
|
if err = extractRootfsFromInitramfs(buff, tempRootfsFile); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// now we are ready to mount rootfs.sqsh
|
|
// create a mount point under tempDir
|
|
rootfsMountPath := filepath.Join(tempDir, "rootfs-mnt")
|
|
|
|
// create the loopback device from the squashfs file
|
|
dev, err := losetup.Attach(tempRootfsFile, 0, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer logErr(func() error {
|
|
if err = dev.Detach(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return dev.Remove()
|
|
})
|
|
|
|
// setup a temporary mount point for the squashfs file and mount it
|
|
m := mount.NewMountPoint(dev.Path(), rootfsMountPath, "squashfs", unix.MS_RDONLY|unix.MS_I_VERSION, "", mount.WithFlags(mount.ReadOnly|mount.Shared))
|
|
|
|
if err = m.Mount(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer logErr(func() error {
|
|
return m.Unmount()
|
|
})
|
|
|
|
// create an overlayfs which contains the rootfs squashfs mount as the base
|
|
// and the extension modules as subsequent lower directories
|
|
overlays := mount.NewMountPoints()
|
|
// writable overlayfs mount inside a container required a tmpfs mount
|
|
overlays.Set("overlays-tmpfs", mount.NewMountPoint("tmpfs", constants.VarSystemOverlaysPath, "tmpfs", unix.MS_I_VERSION, ""))
|
|
|
|
rootfsKernelModulesPath := filepath.Join(rootfsMountPath, constants.DefaultKernelModulesPath)
|
|
|
|
// append the rootfs mount point
|
|
extensionsPathWithKernelModules = append(extensionsPathWithKernelModules, rootfsKernelModulesPath)
|
|
|
|
// create the overlayfs mount point as read write
|
|
mp := mount.NewMountPoint(strings.Join(extensionsPathWithKernelModules, ":"), rootfsKernelModulesPath, "", unix.MS_I_VERSION, "", mount.WithFlags(mount.Overlay|mount.Shared))
|
|
overlays.Set("overlays-mnt", mp)
|
|
|
|
if err = mount.Mount(overlays); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer logErr(func() error {
|
|
return mount.Unmount(overlays)
|
|
})
|
|
|
|
log.Println("running depmod to generate kernel modules dependency tree")
|
|
|
|
if err = depmod(mp.Target()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// we want this temp directory to be present until the extension is compressed later on, so not removing it here
|
|
kernelModulesDependencyTreeStagingDir, err := os.MkdirTemp("", "module-deps")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
kernelModulesDepenencyTreeDirectory := filepath.Join(kernelModulesDependencyTreeStagingDir, constants.DefaultKernelModulesPath)
|
|
|
|
if err := os.MkdirAll(kernelModulesDepenencyTreeDirectory, 0o755); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := findAndMoveKernelModulesDepFiles(kernelModulesDepenencyTreeDirectory, mp.Target()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
kernelModulesDepTreeExtension := newExtension(kernelModulesDependencyTreeStagingDir, "modules.dep")
|
|
kernelModulesDepTreeExtension.Manifest = extensions.Manifest{
|
|
Version: constants.DefaultKernelVersion,
|
|
Metadata: extensions.Metadata{
|
|
Name: "modules.dep",
|
|
Version: constants.DefaultKernelVersion,
|
|
Author: "Talos Machinery",
|
|
Description: "Combined modules.dep for all extensions",
|
|
},
|
|
}
|
|
|
|
return kernelModulesDepTreeExtension, nil
|
|
}
|
|
|
|
func logErr(f func() error) {
|
|
// if file is already closed, ignore the error
|
|
if err := f(); err != nil && !errors.Is(err, os.ErrClosed) {
|
|
log.Println(err)
|
|
}
|
|
}
|
|
|
|
func extractRootfsFromInitramfs(input bytes.Buffer, rootfsFilePath string) error {
|
|
recReader := cpio.Newc.Reader(bytes.NewReader(input.Bytes()))
|
|
|
|
if err := cpio.ForEachRecord(recReader, func(r cpio.Record) error {
|
|
if r.Name != constants.RootfsAsset {
|
|
return nil
|
|
}
|
|
|
|
reader := io.NewSectionReader(r.ReaderAt, 0, int64(r.FileSize))
|
|
f, err := os.Create(rootfsFilePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer logErr(func() error {
|
|
return f.Close()
|
|
})
|
|
|
|
_, err = io.Copy(f, reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return f.Close()
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func depmod(kernelModulesPath string) error {
|
|
baseDir := strings.TrimSuffix(kernelModulesPath, constants.DefaultKernelModulesPath)
|
|
|
|
cmd := exec.Command("depmod", "--all", "--basedir", baseDir, "--config", "/etc/modules.d/10-extra-modules.conf", constants.DefaultKernelVersion)
|
|
cmd.Stderr = os.Stderr
|
|
|
|
return cmd.Run()
|
|
}
|
|
|
|
func findAndMoveKernelModulesDepFiles(dest, kernelModulesPath string) error {
|
|
fs, err := os.ReadDir(kernelModulesPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, f := range fs {
|
|
if f.IsDir() {
|
|
continue
|
|
}
|
|
|
|
if strings.HasPrefix(f.Name(), "modules.") {
|
|
fs, err := f.Info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := moveFile(fs, filepath.Join(kernelModulesPath, f.Name()), filepath.Join(dest, f.Name())); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|