diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-01-19 00:00:13 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-01-19 00:07:56 +0900 |
| commit | 8d06c0235bf01d40d1455fac44519d9cf6983806 (patch) | |
| tree | d6517c0b850a58926a12928c73e11cea603c6d01 /internal/rosa | |
| parent | 4155adc16a7afbec5ca1c23a07ef850f92ab4008 (diff) | |
internal/rosa: busybox binary artifact
This installs a statically linked busybox binary distribution for decompressing the gentoo stage3 tarball, since there is no native xz implementation.
Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/rosa')
| -rw-r--r-- | internal/rosa/busybox.go | 99 | ||||
| -rw-r--r-- | internal/rosa/rosa.go | 39 |
2 files changed, 138 insertions, 0 deletions
diff --git a/internal/rosa/busybox.go b/internal/rosa/busybox.go new file mode 100644 index 00000000..3fe221b9 --- /dev/null +++ b/internal/rosa/busybox.go @@ -0,0 +1,99 @@ +package rosa + +import ( + "fmt" + "io" + "net/http" + "os" + "time" + + "hakurei.app/container/fhs" + "hakurei.app/internal/pkg" +) + +// busyboxBin is a busybox binary distribution installed under bin/busybox. +type busyboxBin struct { + // Underlying busybox binary. + bin pkg.File +} + +// Kind returns the hardcoded [pkg.Kind] value. +func (a busyboxBin) Kind() pkg.Kind { return kindBusyboxBin } + +// Params is a noop. +func (a busyboxBin) Params(*pkg.IContext) {} + +// Dependencies returns the underlying busybox [pkg.File]. +func (a busyboxBin) Dependencies() []pkg.Artifact { + return []pkg.Artifact{a.bin} +} + +// String returns the reporting name of the underlying file prefixed with expand. +func (a busyboxBin) String() string { + return "expand-" + a.bin.(fmt.Stringer).String() +} + +// Cure installs the underlying busybox [pkg.File] to bin/busybox. +func (a busyboxBin) Cure(t *pkg.TContext) (err error) { + var r io.ReadCloser + if r, err = t.Open(a.bin); err != nil { + return + } + defer func() { + closeErr := r.Close() + if err == nil { + err = closeErr + } + }() + + binDir := t.GetWorkDir().Append("bin") + if err = os.MkdirAll(binDir.String(), 0700); err != nil { + return + } + + var w *os.File + if w, err = os.OpenFile( + binDir.Append("busybox").String(), + os.O_WRONLY|os.O_CREATE|os.O_EXCL, + 0500, + ); err != nil { + return + } + defer func() { + closeErr := w.Close() + if err == nil { + err = closeErr + } + }() + + _, err = io.Copy(w, r) + return +} + +// newBusyboxBin returns a [pkg.Artifact] containing a busybox installation from +// the https://busybox.net/downloads/binaries/ binary release. +func newBusyboxBin() pkg.Artifact { + const ( + version = "1.35.0" + checksum = "L7OBIsPu9enNHn7FqpBT1kOg_mCLNmetSeNMA3i4Y60Z5jTgnlX3qX3zcQtLx5AB" + ) + return pkg.NewExec( + "busybox-bin-"+version, nil, pkg.ExecTimeoutMax, fhs.AbsRoot, []string{ + "PATH=/system/bin", + }, + AbsSystem.Append("bin", "busybox"), + []string{"hush", "-c", "" + + "busybox mkdir -p /work/system/bin/ && " + + "busybox cp /system/bin/busybox /work/system/bin/ && " + + "busybox --install -s /work/system/bin/"}, + pkg.Path(AbsSystem, true, busyboxBin{pkg.NewHTTPGet( + &http.Client{Transport: &http.Transport{ + // busybox website is really slow to respond + TLSHandshakeTimeout: 2 * time.Minute, + }}, + "https://busybox.net/downloads/binaries/"+ + version+"-"+linuxArch()+"-linux-musl/busybox", + mustDecode(checksum), + )}), + ) +} diff --git a/internal/rosa/rosa.go b/internal/rosa/rosa.go index 7ddf4143..cec415aa 100644 --- a/internal/rosa/rosa.go +++ b/internal/rosa/rosa.go @@ -2,10 +2,49 @@ package rosa import ( + "log" + "runtime" + + "hakurei.app/container/fhs" "hakurei.app/internal/pkg" ) const ( // kindEtc is the kind of [pkg.Artifact] of cureEtc. kindEtc = iota + pkg.KindCustomOffset + + // kindBusyboxBin is the kind of [pkg.Artifact] of busyboxBin. + kindBusyboxBin ) + +// mustDecode is like [pkg.MustDecode], but replaces the zero value and prints +// a warning. +func mustDecode(s string) pkg.Checksum { + var fallback = pkg.Checksum{} + if s == "" { + log.Println( + "falling back to", + pkg.Encode(fallback), + "for unpopulated checksum", + ) + return fallback + } + return pkg.MustDecode(s) +} + +var ( + // AbsSystem is the Rosa OS installation prefix. + AbsSystem = fhs.AbsRoot.Append("system") +) + +// linuxArch returns the architecture name used by linux corresponding to +// [runtime.GOARCH]. +func linuxArch() string { + switch runtime.GOARCH { + case "amd64": + return "x86_64" + + default: + panic("unsupported target " + runtime.GOARCH) + } +} |
