diff --git a/uimage/initramfs/archive.go b/uimage/initramfs/archive.go index 893e570..8e29229 100644 --- a/uimage/initramfs/archive.go +++ b/uimage/initramfs/archive.go @@ -57,6 +57,9 @@ type Opts struct { // If this is false, the "init" file in BaseArchive will be renamed // "inito" (for init-original) in the output archive. UseExistingInit bool + + // Records are additional CPIO records to include in the initramfs. + Records []cpio.Record } // Write uses the given options to determine which files to write to the output @@ -99,6 +102,12 @@ func Write(opts *Opts) error { } } + for _, r := range opts.Records { + if err := opts.Files.AddRecord(r); err != nil { + return err + } + } + out, err := opts.OutputFile.OpenWriter() if err != nil { return err diff --git a/uimage/initramfs/files_test.go b/uimage/initramfs/files_test.go index 77c1c03..9a04ada 100644 --- a/uimage/initramfs/files_test.go +++ b/uimage/initramfs/files_test.go @@ -636,6 +636,48 @@ func TestOptsWrite(t *testing.T) { cpio.Trailer: cpio.TrailerRecord, }, }, + { + desc: "base archive with init and extra records conflict", + opts: &Opts{ + Files: &Files{ + Records: map[string]cpio.Record{ + "init": cpio.StaticFile("init", "huh", 0o111), + }, + }, + BaseArchive: &Archive{Archive: archive(t, + cpio.StaticFile("init", "boo", 0o555), + )}, + Records: []cpio.Record{ + cpio.StaticFile("init", "huh", 0o111), + }, + UseExistingInit: true, + }, + errs: []error{os.ErrExist}, + output: &cpio.Archive{ + Files: make(map[string]cpio.Record), + }, + }, + { + desc: "extra records", + opts: &Opts{ + Files: &Files{ + Records: map[string]cpio.Record{ + "init": cpio.StaticFile("init", "huh", 0o111), + }, + }, + Records: []cpio.Record{ + cpio.StaticFile("etc/foo", "huh", 0o111), + }, + }, + output: &cpio.Archive{ + Files: make(map[string]cpio.Record), + }, + want: Records{ + "init": cpio.StaticFile("init", "boo", 0o555), + "etc/foo": cpio.StaticFile("etc/foo", "huh", 0o111), + cpio.Trailer: cpio.TrailerRecord, + }, + }, } { t.Run(fmt.Sprintf("Test %02d (%s)", i, tt.desc), func(t *testing.T) { tt.opts.OutputFile = &Archive{tt.output} diff --git a/uimage/uimage.go b/uimage/uimage.go index 49e7ff7..b39312e 100644 --- a/uimage/uimage.go +++ b/uimage/uimage.go @@ -236,6 +236,9 @@ type Opts struct { // // This must be specified to have a default shell. DefaultShell string + + // Records are additional CPIO records to include in the initramfs. + Records []cpio.Record } // Modifier modifies uimage options. @@ -503,6 +506,14 @@ func WithTempDir(dir string) Modifier { } } +// WithRecord adds CPIO records to include in the initramfs. +func WithRecord(r ...cpio.Record) Modifier { + return func(o *Opts) error { + o.Records = append(o.Records, r...) + return nil + } +} + // Create creates an initramfs from mods specifications. func Create(l *llog.Logger, mods ...Modifier) error { o, err := OptionsFor(mods...) @@ -571,6 +582,7 @@ func CreateInitramfs(l *llog.Logger, opts Opts) error { OutputFile: opts.OutputFile, BaseArchive: opts.BaseArchive, UseExistingInit: opts.UseExistingInit, + Records: opts.Records, } if err := ParseExtraFiles(l, archive.Files, opts.ExtraFiles, !opts.SkipLDD); err != nil { return err diff --git a/uimage/uimage_test.go b/uimage/uimage_test.go index cdb4838..e76fea0 100644 --- a/uimage/uimage_test.go +++ b/uimage/uimage_test.go @@ -505,6 +505,46 @@ func TestCreateInitramfs(t *testing.T) { itest.IsEmpty{}, }, }, + { + name: "extra records conflict", + opts: Opts{ + Env: golang.Default(golang.DisableCGO()), + TempDir: dir, + InitCmd: "init", + DefaultShell: "ls", + Records: []cpio.Record{ + cpio.StaticFile("bbin/ls", "foo", 0o111), + }, + Commands: BusyboxCmds( + "github.com/u-root/u-root/cmds/core/init", + "github.com/u-root/u-root/cmds/core/ls", + ), + }, + errs: []error{os.ErrExist}, + validators: []itest.ArchiveValidator{ + itest.IsEmpty{}, + }, + }, + { + name: "extra records", + opts: Opts{ + Env: golang.Default(golang.DisableCGO()), + TempDir: dir, + InitCmd: "init", + DefaultShell: "ls", + Records: []cpio.Record{ + cpio.StaticFile("etc/foo", "foo", 0o111), + }, + Commands: BusyboxCmds( + "github.com/u-root/u-root/cmds/core/init", + "github.com/u-root/u-root/cmds/core/ls", + ), + }, + validators: []itest.ArchiveValidator{ + itest.HasFile{Path: "bbin/bb"}, + itest.HasRecord{R: cpio.StaticFile("etc/foo", "foo", 0o111)}, + }, + }, } { t.Run(fmt.Sprintf("Test %d [%s]", i, tt.name), func(t *testing.T) { archive := cpio.InMemArchive() @@ -992,6 +1032,42 @@ func TestCreateInitramfsWithAPI(t *testing.T) { itest.IsEmpty{}, }, }, + { + name: "extra records conflict", + opts: []Modifier{ + WithTempDir(dir), + WithEnv(golang.DisableCGO()), + WithInit("init"), + WithShell("ls"), + WithRecord(cpio.StaticFile("bbin/ls", "foo", 0o111)), + WithBusyboxCommands( + "github.com/u-root/u-root/cmds/core/init", + "github.com/u-root/u-root/cmds/core/ls", + ), + }, + errs: []error{os.ErrExist}, + validators: []itest.ArchiveValidator{ + itest.IsEmpty{}, + }, + }, + { + name: "extra records", + opts: []Modifier{ + WithTempDir(dir), + WithEnv(golang.DisableCGO()), + WithInit("init"), + WithShell("ls"), + WithRecord(cpio.StaticFile("etc/foo", "foo", 0o111)), + WithBusyboxCommands( + "github.com/u-root/u-root/cmds/core/init", + "github.com/u-root/u-root/cmds/core/ls", + ), + }, + validators: []itest.ArchiveValidator{ + itest.HasFile{Path: "bbin/bb"}, + itest.HasRecord{R: cpio.StaticFile("etc/foo", "foo", 0o111)}, + }, + }, } { t.Run(fmt.Sprintf("Test %d [%s]", i, tt.name), func(t *testing.T) { archive := cpio.InMemArchive()