Skip to content

Commit

Permalink
New initramfs API
Browse files Browse the repository at this point in the history
Signed-off-by: Chris Koch <[email protected]>
  • Loading branch information
hugelgupf committed Feb 12, 2024
1 parent f79806f commit b6d23ac
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 243 deletions.
85 changes: 47 additions & 38 deletions cmd/mkuimage/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,15 @@ var (

// Flags for u-root builder.
var (
build, format, tmpDir, base, outputPath *string
uinitCmd, initCmd *string
defaultShell *string
useExistingInit *bool
noCommands *bool
extraFiles multiFlag
statsOutputPath *string
statsLabel *string
shellbang *bool
build, format, tmpDir, basePath, outputPath *string
uinitCmd, initCmd *string
defaultShell *string
useExistingInit *bool
noCommands *bool
extraFiles multiFlag
statsOutputPath *string
statsLabel *string
shellbang *bool
// For the new "filepath only" logic.
urootSourceDir *string
)
Expand All @@ -74,7 +74,7 @@ func init() {

tmpDir = flag.String("tmpdir", "", "Temporary directory to put binaries in.")

base = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
basePath = flag.String("base", "", "Base archive to add files to. By default, this is a couple of directories like /bin, /etc, etc. u-root has a default internally supplied set of files; use base=/dev/null if you don't want any base files.")
useExistingInit = flag.Bool("useinit", false, "Use existing init from base archive (only if --base was specified).")
outputPath = flag.String("o", "", "Path to output initramfs file.")

Expand Down Expand Up @@ -253,6 +253,33 @@ func isRecommendedVersion(v string) bool {
return false
}

func GetReader(format string, path string) initramfs.ReadOpener {
switch format {
case "cpio":
return &initramfs.CPIOFile{Path: path}
default:
return nil
}
}

func GetWriter(format string, path string) initramfs.WriteOpener {
switch format {
case "cpio":
return &initramfs.CPIOFile{Path: path}
case "dir":
return &initramfs.Dir{Path: path}
default:
return nil
}
}

func defaultFile(env *golang.Environ) string {
if len(env.GOOS) == 0 || len(env.GOARCH) == 0 {
return "/tmp/initramfs.cpio"
}
return fmt.Sprintf("/tmp/initramfs.%s_%s.cpio", env.GOOS, env.GOARCH)
}

// Main is a separate function so defers are run on return, which they wouldn't
// on exit.
func Main(l ulog.Logger, env *golang.Environ, buildOpts *golang.BuildOpts) error {
Expand All @@ -269,34 +296,16 @@ func Main(l ulog.Logger, env *golang.Environ, buildOpts *golang.BuildOpts) error
v, recommendedVersions, recommendedVersions[0])
}

archiver, err := initramfs.GetArchiver(*format)
if err != nil {
return err
var output initramfs.WriteOpener
output = &initramfs.CPIOFile{Path: defaultFile(env)}
if *outputPath != "" {
output = GetWriter(*format, *outputPath)
}

// Open the target initramfs file.
if *outputPath == "" {
*outputPath, err = archiver.CreateDefault(env)
if err != nil {
return err
}
l.Printf("Output path is %v", *outputPath)
}
w, err := archiver.OpenWriter(*outputPath)
if err != nil {
return err
}

var baseFile initramfs.Reader
if *base != "" {
bf, err := os.Open(*base)
if err != nil {
return err
}
defer bf.Close()
baseFile = archiver.Reader(bf)
} else {
baseFile = uroot.DefaultRamfs().Reader()
var base initramfs.ReadOpener
base = &initramfs.Archive{Archive: uroot.DefaultRamfs()}
if *basePath != "" {
base = GetReader(*format, *basePath)
}

tempDir := *tmpDir
Expand Down Expand Up @@ -363,8 +372,8 @@ func Main(l ulog.Logger, env *golang.Environ, buildOpts *golang.BuildOpts) error
UrootSource: *urootSourceDir,
TempDir: tempDir,
ExtraFiles: extraFiles,
OutputFile: w,
BaseArchive: baseFile,
OutputFile: output,
BaseArchive: base,
UseExistingInit: *useExistingInit,
InitCmd: initCommand,
DefaultShell: *defaultShell,
Expand Down
68 changes: 19 additions & 49 deletions uroot/initramfs/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,19 @@
package initramfs

import (
"fmt"
"io"

"github.com/u-root/gobusybox/src/pkg/golang"
"github.com/u-root/mkuimage/cpio"
)

var (
// CPIO creates files in a CPIO file.
CPIO = CPIOArchiver{
RecordFormat: cpio.Newc,
}

// Dir writes "archived" files to a directory.
Dir = DirArchiver{}

// Archivers are the supported initramfs archivers at the moment.
//
// - cpio: writes the initramfs to a cpio.
// - dir: writes the initramfs relative to a specified directory.
Archivers = map[string]Archiver{
"cpio": CPIO,
"dir": Dir,
}
)

// Archiver is an archive format that builds an archive using a given set of
// files.
type Archiver interface {
// CreateDefault creates a default file that can be passed as path to OpenWriter.
CreateDefault(env *golang.Environ) (string, error)

// OpenWriter opens an archive writer at `path`.
OpenWriter(path string) (Writer, error)

// Reader returns a Reader that allows reading files from a file.
Reader(file io.ReaderAt) Reader
// ReadOpener opens a cpio.RecordReader.
type ReadOpener interface {
OpenReader() (cpio.RecordReader, error)
}

// GetArchiver finds a registered initramfs archiver by name.
//
// Good to use with command-line arguments.
func GetArchiver(name string) (Archiver, error) {
archiver, ok := Archivers[name]
if !ok {
return nil, fmt.Errorf("couldn't find archival format %q", name)
}
return archiver, nil
// WriteOpener opens a Writer.
type WriteOpener interface {
OpenWriter() (Writer, error)
}

// Writer is an initramfs archive that files can be written to.
Expand All @@ -64,9 +29,6 @@ type Writer interface {
Finish() error
}

// Reader is an object that files can be read from.
type Reader cpio.RecordReader

// Opts are options for building an initramfs archive.
type Opts struct {
// Files are the files to be included.
Expand All @@ -76,12 +38,12 @@ type Opts struct {
*Files

// OutputFile is the file to write to.
OutputFile Writer
OutputFile WriteOpener

// BaseArchive is an existing archive to add files to.
//
// BaseArchive may be nil.
BaseArchive Reader
BaseArchive ReadOpener

// UseExistingInit determines whether the init from BaseArchive is used
// or not, if BaseArchive is specified.
Expand All @@ -96,6 +58,10 @@ type Opts struct {
func Write(opts *Opts) error {
// Write base archive.
if opts.BaseArchive != nil {
base, err := opts.BaseArchive.OpenReader()
if err != nil {
return err
}
transform := cpio.MakeReproducible

// Rename init to inito if user doesn't want the existing init.
Expand All @@ -114,7 +80,7 @@ func Write(opts *Opts) error {
}

for {
f, err := opts.BaseArchive.ReadRecord()
f, err := base.ReadRecord()
if err == io.EOF {
break
}
Expand All @@ -127,8 +93,12 @@ func Write(opts *Opts) error {
}
}

if err := opts.Files.WriteTo(opts.OutputFile); err != nil {
out, err := opts.OutputFile.OpenWriter()
if err != nil {
return err
}
if err := opts.Files.WriteTo(out); err != nil {
return err
}
return opts.OutputFile.Finish()
return out.Finish()
}
67 changes: 42 additions & 25 deletions uroot/initramfs/cpio.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,72 @@ package initramfs

import (
"fmt"
"io"
"os"

"github.com/u-root/gobusybox/src/pkg/golang"
"github.com/u-root/mkuimage/cpio"
)

// CPIOArchiver is an implementation of Archiver for the cpio format.
type CPIOArchiver struct {
cpio.RecordFormat
// CPIOFile opens a Reader or Writer that reads/writes files from/to a CPIO archive at the given path.
type CPIOFile struct {
Path string
}

// CreateDefault chooses /tmp/initramfs.cpio or /tmp/initramfs.GOOS_GOARCH.cpio
// if available.
func (ca CPIOArchiver) CreateDefault(env *golang.Environ) (string, error) {
if len(env.GOOS) == 0 || len(env.GOARCH) == 0 {
return "/tmp/initramfs.cpio", nil
var _ ReadOpener = &CPIOFile{}
var _ WriteOpener = &CPIOFile{}

// OpenWriter opens c.Path for writing.
func (c *CPIOFile) OpenWriter() (Writer, error) {
if len(c.Path) == 0 {
return nil, fmt.Errorf("path is required")
}
f, err := os.OpenFile(c.Path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
if err != nil {
return nil, err
}
return fmt.Sprintf("/tmp/initramfs.%s_%s.cpio", env.GOOS, env.GOARCH), nil
return cpioWriter{cpio.Newc.Writer(f), f}, nil
}

// OpenWriter opens `path` as the correct file type and returns an
// Writer pointing to `path`.
func (ca CPIOArchiver) OpenWriter(path string) (Writer, error) {
if len(path) == 0 {
// OpenReader opens c.Path for reading.
func (c *CPIOFile) OpenReader() (cpio.RecordReader, error) {
if len(c.Path) == 0 {
return nil, fmt.Errorf("path is required")
}
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)
f, err := os.Open(c.Path)
if err != nil {
return nil, err
}
return osWriter{ca.RecordFormat.Writer(f), f}, nil
return cpio.Newc.Reader(f), nil
}

// Archive opens a Reader that reads files from an in-memory archive.
type Archive struct {
*cpio.Archive
}

var _ ReadOpener = &Archive{}

// OpenWriter writes to the archive.
func (a *Archive) OpenWriter() (Writer, error) {
return cpioWriter{a.Archive, nil}, nil
}

// OpenReader opens the archive for reading.
func (a *Archive) OpenReader() (cpio.RecordReader, error) {
return a.Archive.Reader(), nil
}

// osWriter implements Writer.
type osWriter struct {
type cpioWriter struct {
cpio.RecordWriter

f *os.File
}

// Finish implements Writer.Finish.
func (o osWriter) Finish() error {
func (o cpioWriter) Finish() error {
err := cpio.WriteTrailer(o)
o.f.Close()
if o.f != nil {
o.f.Close()
}
return err
}

// Reader implements Archiver.Reader.
func (ca CPIOArchiver) Reader(r io.ReaderAt) Reader {
return ca.RecordFormat.Reader(r)
}
31 changes: 8 additions & 23 deletions uroot/initramfs/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,27 @@ package initramfs
import (
"errors"
"fmt"
"io"
"os"

"github.com/u-root/gobusybox/src/pkg/golang"
"github.com/u-root/mkuimage/cpio"
)

// DirArchiver implements Archiver for a directory.
type DirArchiver struct{}

// Reader implements Archiver.Reader.
//
// Currently unsupported for directories.
func (da DirArchiver) Reader(io.ReaderAt) Reader {
return nil
// Dir opens a Writer that writes all archive files to the given directory.
type Dir struct {
Path string
}

// CreateDefault creates a tmpdir using os.MkdirTemp prefixed with mkuimage- or
// mkuimage-GOOS-GOARCH- if available.
func (da DirArchiver) CreateDefault(env *golang.Environ) (string, error) {
name := "mkuimage-"
if len(env.GOOS) != 0 && len(env.GOARCH) != 0 {
name = fmt.Sprintf("mkuimage-%s-%s-", env.GOOS, env.GOARCH)
}
return os.MkdirTemp("", name)
}
var _ WriteOpener = &Dir{}

// OpenWriter implements Archiver.OpenWriter.
func (da DirArchiver) OpenWriter(path string) (Writer, error) {
if len(path) == 0 {
func (d *Dir) OpenWriter() (Writer, error) {
if len(d.Path) == 0 {
return nil, fmt.Errorf("path is required")
}
if err := os.MkdirAll(path, 0o755); err != nil && !errors.Is(err, os.ErrExist) {
if err := os.MkdirAll(d.Path, 0o755); err != nil && !errors.Is(err, os.ErrExist) {
return nil, err
}
return dirWriter{path}, nil
return dirWriter{d.Path}, nil
}

// dirWriter implements Writer.
Expand Down
Loading

0 comments on commit b6d23ac

Please sign in to comment.