mirror of
https://github.com/siderolabs/talos.git
synced 2026-04-07 14:51:08 +02:00
See: * https://github.com/siderolabs/go-blockdevice/pull/147 * https://github.com/siderolabs/go-cmd/pull/9 Lots of changes through the code as I deprecated `Run`, `RunWithContext` methods and allow only new `RunWithOptions` to clean up the library usage. Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
169 lines
3.7 KiB
Go
169 lines
3.7 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 makefs
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/siderolabs/go-cmd/pkg/cmd"
|
|
|
|
"github.com/siderolabs/talos/pkg/imager/utils"
|
|
)
|
|
|
|
const (
|
|
// FilesystemTypeEXT4 is the filesystem type for EXT4.
|
|
FilesystemTypeEXT4 = "ext4"
|
|
)
|
|
|
|
// Ext4 creates a ext4 filesystem on the specified partition.
|
|
//
|
|
//nolint:gocyclo
|
|
func Ext4(ctx context.Context, partname string, setters ...Option) error {
|
|
if partname == "" {
|
|
return errors.New("missing path to disk")
|
|
}
|
|
|
|
opts := NewDefaultOptions(setters...)
|
|
|
|
var args []string
|
|
|
|
if opts.Label != "" {
|
|
args = append(args, "-L", opts.Label)
|
|
}
|
|
|
|
if opts.Force {
|
|
args = append(args, "-F")
|
|
}
|
|
|
|
if opts.Reproducible {
|
|
if opts.Label == "" {
|
|
return errors.New("label must be set for reproducible ext4 filesystem")
|
|
}
|
|
|
|
partitionGUID := GUIDFromLabel(opts.Label)
|
|
|
|
args = append(args, "-U", partitionGUID.String())
|
|
args = append(args, "-E", fmt.Sprintf("hash_seed=%s", partitionGUID.String()))
|
|
}
|
|
|
|
var errCh chan error
|
|
|
|
// Use a tar archive instead of passing a source directory directly to mke2fs.
|
|
// This allows us to force all files in the image to be owned by root (uid/gid 0)
|
|
// regardless of their ownership on the host, enabling rootless operation without
|
|
// requiring elevated privileges.
|
|
if opts.SourceDirectory != "" {
|
|
errCh = make(chan error, 1)
|
|
|
|
pr, pw, err := os.Pipe()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create pipe: %w", err)
|
|
}
|
|
|
|
defer pr.Close() //nolint:errcheck
|
|
|
|
args = append(args, "-d", "-")
|
|
|
|
ctx = cmd.WithStdin(ctx, pr)
|
|
|
|
go func() {
|
|
defer pw.Close() //nolint:errcheck
|
|
|
|
errCh <- handleTarArchive(ctx, opts.SourceDirectory, pw)
|
|
}()
|
|
}
|
|
|
|
args = append(args, partname)
|
|
|
|
opts.Printf("creating ext4 filesystem on %s with args: %v", partname, args)
|
|
|
|
_, err := cmd.RunWithOptions(ctx, "mkfs.ext4", args)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if errCh != nil {
|
|
return <-errCh
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Ext4Resize expands a ext4 filesystem to the maximum possible.
|
|
func Ext4Resize(ctx context.Context, partname string) error {
|
|
// resizing the filesystem requires a check first
|
|
if err := Ext4Repair(ctx, partname); err != nil {
|
|
return fmt.Errorf("failed to repair before growing ext4 filesystem: %w", err)
|
|
}
|
|
|
|
_, err := cmd.RunWithOptions(ctx, "resize2fs", []string{partname})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to grow ext4 filesystem: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Ext4Repair repairs a ext4 filesystem.
|
|
func Ext4Repair(ctx context.Context, partname string) error {
|
|
_, err := cmd.RunWithOptions(ctx, "e2fsck", []string{"-f", "-p", partname})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to repair ext4 filesystem: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// handleTarArchive creates a tar archive from the sourceDir and writes it to the provided
|
|
// io.WriteCloser.
|
|
func handleTarArchive(ctx context.Context, sourceDir string, w io.WriteCloser) error {
|
|
timestamp, ok, err := utils.SourceDateEpoch()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get SOURCE_DATE_EPOCH: %w", err)
|
|
}
|
|
|
|
if !ok {
|
|
timestamp = time.Now().Unix()
|
|
}
|
|
|
|
cmd := exec.CommandContext(
|
|
ctx,
|
|
"tar",
|
|
"-cf",
|
|
"-",
|
|
"-C",
|
|
sourceDir,
|
|
"--sort=name",
|
|
"--owner=0",
|
|
"--group=0",
|
|
"--numeric-owner",
|
|
"--mtime=@"+strconv.FormatInt(timestamp, 10),
|
|
".",
|
|
)
|
|
|
|
cmd.Stdout = w
|
|
cmd.Stderr = os.Stderr
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return fmt.Errorf("failed to start tar command: %w", err)
|
|
}
|
|
|
|
closeErr := w.Close()
|
|
waitErr := cmd.Wait()
|
|
|
|
if closeErr != nil {
|
|
return fmt.Errorf("failed to close pipe writer: %w", closeErr)
|
|
}
|
|
|
|
return waitErr
|
|
}
|