Compare commits
28 Commits
9aad98d409
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
0cf0e18e35
|
|||
|
ee5c0dd135
|
|||
|
92c48d82e2
|
|||
|
c79a4fe7f8
|
|||
|
0aeb2bccfb
|
|||
|
50e079b99f
|
|||
|
fb2cb5005a
|
|||
|
6e73c28a92
|
|||
|
2c08aa3674
|
|||
|
1af73ae7b4
|
|||
|
c9aa5e04b1
|
|||
|
70a38bd3b0
|
|||
|
533b15da89
|
|||
|
a890e1d0e5
|
|||
|
e3520835bb
|
|||
|
0e56847754
|
|||
|
145d03b366
|
|||
|
2886228d40
|
|||
|
e1e499b79e
|
|||
|
65b7dd8b37
|
|||
|
8d72b9e5bd
|
|||
|
8a3c3d145a
|
|||
|
575ef307ad
|
|||
|
d4144fcf7f
|
|||
|
bad66facbc
|
|||
|
4aba014eac
|
|||
|
779ba994ce
|
|||
|
917be2de93
|
9
all.sh
9
all.sh
@@ -1,10 +1,3 @@
|
|||||||
#!/bin/sh -e
|
#!/bin/sh -e
|
||||||
|
|
||||||
TOOLCHAIN_VERSION="$(go version)"
|
HAKUREI_DIST_MAKE='' exec "$(dirname -- "$0")/cmd/dist/dist.sh"
|
||||||
cd "$(dirname -- "$0")/"
|
|
||||||
echo "Building cmd/dist using ${TOOLCHAIN_VERSION}."
|
|
||||||
FLAGS=''
|
|
||||||
if test -n "$VERBOSE"; then
|
|
||||||
FLAGS="$FLAGS -v"
|
|
||||||
fi
|
|
||||||
go run $FLAGS --tags=dist ./cmd/dist
|
|
||||||
|
|||||||
10
cmd/dist/dist.sh
vendored
Executable file
10
cmd/dist/dist.sh
vendored
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
TOOLCHAIN_VERSION="$(go version)"
|
||||||
|
cd "$(dirname -- "$0")/../.."
|
||||||
|
echo "Building cmd/dist using ${TOOLCHAIN_VERSION}."
|
||||||
|
FLAGS=''
|
||||||
|
if test -n "$VERBOSE"; then
|
||||||
|
FLAGS="$FLAGS -v"
|
||||||
|
fi
|
||||||
|
go run $FLAGS --tags=dist ./cmd/dist
|
||||||
17
cmd/dist/main.go
vendored
17
cmd/dist/main.go
vendored
@@ -46,6 +46,7 @@ func main() {
|
|||||||
log.SetPrefix("")
|
log.SetPrefix("")
|
||||||
|
|
||||||
verbose := os.Getenv("VERBOSE") != ""
|
verbose := os.Getenv("VERBOSE") != ""
|
||||||
|
runTests := os.Getenv("HAKUREI_DIST_MAKE") == ""
|
||||||
version := getenv("HAKUREI_VERSION", "untagged")
|
version := getenv("HAKUREI_VERSION", "untagged")
|
||||||
prefix := getenv("PREFIX", "/usr")
|
prefix := getenv("PREFIX", "/usr")
|
||||||
destdir := getenv("DESTDIR", "dist")
|
destdir := getenv("DESTDIR", "dist")
|
||||||
@@ -101,13 +102,15 @@ func main() {
|
|||||||
)
|
)
|
||||||
log.Println()
|
log.Println()
|
||||||
|
|
||||||
log.Println("##### Testing Hakurei.")
|
if runTests {
|
||||||
mustRun(
|
log.Println("##### Testing Hakurei.")
|
||||||
ctx, "go", "test",
|
mustRun(
|
||||||
"-ldflags=-buildid= -linkmode external -extldflags=-static",
|
ctx, "go", "test",
|
||||||
"./...",
|
"-ldflags=-buildid= -linkmode external -extldflags=-static",
|
||||||
)
|
"./...",
|
||||||
log.Println()
|
)
|
||||||
|
log.Println()
|
||||||
|
}
|
||||||
|
|
||||||
log.Println("##### Creating distribution.")
|
log.Println("##### Creating distribution.")
|
||||||
const suffix = ".tar.gz"
|
const suffix = ".tar.gz"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"hakurei.app/check"
|
"hakurei.app/check"
|
||||||
|
"hakurei.app/container"
|
||||||
"hakurei.app/internal/pkg"
|
"hakurei.app/internal/pkg"
|
||||||
"hakurei.app/message"
|
"hakurei.app/message"
|
||||||
)
|
)
|
||||||
@@ -19,9 +20,15 @@ type cache struct {
|
|||||||
// Should generally not be used directly.
|
// Should generally not be used directly.
|
||||||
c *pkg.Cache
|
c *pkg.Cache
|
||||||
|
|
||||||
cures, jobs int
|
cures, jobs int
|
||||||
hostAbstract, idle bool
|
// Primarily to work around missing landlock LSM.
|
||||||
verboseInit bool
|
hostAbstract bool
|
||||||
|
// Set SCHED_IDLE.
|
||||||
|
idle bool
|
||||||
|
// Unset [pkg.CSuppressInit].
|
||||||
|
verboseInit bool
|
||||||
|
// Loaded artifact of [rosa.QEMU].
|
||||||
|
qemu pkg.Artifact
|
||||||
|
|
||||||
base string
|
base string
|
||||||
}
|
}
|
||||||
@@ -74,6 +81,39 @@ func (cache *cache) open() (err error) {
|
|||||||
cache.jobs,
|
cache.jobs,
|
||||||
base,
|
base,
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
done <- struct{}{}
|
||||||
|
|
||||||
|
if cache.qemu != nil {
|
||||||
|
var pathname *check.Absolute
|
||||||
|
pathname, _, err = cache.c.Cure(cache.qemu)
|
||||||
|
if err != nil {
|
||||||
|
cache.c.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg.RegisterArch("riscv64", container.BinfmtEntry{
|
||||||
|
Offset: 0,
|
||||||
|
Magic: "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00",
|
||||||
|
Mask: "\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff",
|
||||||
|
Interpreter: pathname.Append(
|
||||||
|
"system/bin",
|
||||||
|
"qemu-riscv64",
|
||||||
|
),
|
||||||
|
})
|
||||||
|
pkg.RegisterArch("arm64", container.BinfmtEntry{
|
||||||
|
Offset: 0,
|
||||||
|
Magic: "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00",
|
||||||
|
Mask: "\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff",
|
||||||
|
Interpreter: pathname.Append(
|
||||||
|
"system/bin",
|
||||||
|
"qemu-aarch64",
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,9 +67,13 @@ func main() {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
flagQuiet bool
|
flagQuiet bool
|
||||||
|
flagQEMU bool
|
||||||
|
flagArch string
|
||||||
flagCheck bool
|
flagCheck bool
|
||||||
flagLTO bool
|
flagLTO bool
|
||||||
|
|
||||||
|
flagCrossOverride int
|
||||||
|
|
||||||
addr net.UnixAddr
|
addr net.UnixAddr
|
||||||
)
|
)
|
||||||
c := command.New(os.Stderr, log.Printf, "mbf", func([]string) error {
|
c := command.New(os.Stderr, log.Printf, "mbf", func([]string) error {
|
||||||
@@ -93,13 +97,36 @@ func main() {
|
|||||||
if !flagLTO {
|
if !flagLTO {
|
||||||
flags |= rosa.OptLLVMNoLTO
|
flags |= rosa.OptLLVMNoLTO
|
||||||
}
|
}
|
||||||
rosa.DropCaches(flags)
|
rosa.DropCaches("", flags)
|
||||||
|
cross := flagArch != "" && flagArch != runtime.GOARCH
|
||||||
|
if flagQEMU || cross {
|
||||||
|
cm.qemu = rosa.Std.Load(rosa.QEMU)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cross {
|
||||||
|
if flagCrossOverride != -1 {
|
||||||
|
flags = flagCrossOverride
|
||||||
|
}
|
||||||
|
|
||||||
|
rosa.DropCaches(flagArch, flags)
|
||||||
|
if !rosa.HasStage0() {
|
||||||
|
return pkg.UnsupportedArchError(flagArch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}).Flag(
|
}).Flag(
|
||||||
&flagQuiet,
|
&flagQuiet,
|
||||||
"q", command.BoolFlag(false),
|
"q", command.BoolFlag(false),
|
||||||
"Do not print cure messages",
|
"Do not print cure messages",
|
||||||
|
).Flag(
|
||||||
|
&flagQEMU,
|
||||||
|
"register", command.BoolFlag(false),
|
||||||
|
"Enable additional target architectures",
|
||||||
|
).Flag(
|
||||||
|
&flagArch,
|
||||||
|
"arch", command.StringFlag(runtime.GOARCH),
|
||||||
|
"Target architecture",
|
||||||
).Flag(
|
).Flag(
|
||||||
&flagLTO,
|
&flagLTO,
|
||||||
"lto", command.BoolFlag(false),
|
"lto", command.BoolFlag(false),
|
||||||
@@ -108,6 +135,10 @@ func main() {
|
|||||||
&flagCheck,
|
&flagCheck,
|
||||||
"check", command.BoolFlag(true),
|
"check", command.BoolFlag(true),
|
||||||
"Run test suites",
|
"Run test suites",
|
||||||
|
).Flag(
|
||||||
|
&flagCrossOverride,
|
||||||
|
"cross-flags", command.IntFlag(-1),
|
||||||
|
"Override non-native target preset flags",
|
||||||
).Flag(
|
).Flag(
|
||||||
&cm.verboseInit,
|
&cm.verboseInit,
|
||||||
"v", command.BoolFlag(false),
|
"v", command.BoolFlag(false),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
rosa.DropCaches(rosa.OptLLVMNoLTO)
|
rosa.DropCaches("", rosa.OptLLVMNoLTO)
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,11 +20,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
virtualisation = {
|
virtualisation = {
|
||||||
|
# Hopefully reduces spurious test failures:
|
||||||
|
memorySize = if pkgs.stdenv.hostPlatform.is32bit then 2046 else 8192;
|
||||||
|
|
||||||
diskSize = 6 * 1024;
|
diskSize = 6 * 1024;
|
||||||
|
|
||||||
qemu.options = [
|
qemu.options = [
|
||||||
# Increase test performance:
|
# Increase test performance:
|
||||||
"-smp 8"
|
"-smp 16"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ testers.nixosTest {
|
|||||||
# For go tests:
|
# For go tests:
|
||||||
(pkgs.writeShellScriptBin "sharefs-workload-hakurei-tests" ''
|
(pkgs.writeShellScriptBin "sharefs-workload-hakurei-tests" ''
|
||||||
cp -r "${self.packages.${system}.hakurei.src}" "/sdcard/hakurei" && cd "/sdcard/hakurei"
|
cp -r "${self.packages.${system}.hakurei.src}" "/sdcard/hakurei" && cd "/sdcard/hakurei"
|
||||||
${fhs}/bin/hakurei-fhs -c 'CC="clang -O3 -Werror" go test ./...'
|
${fhs}/bin/hakurei-fhs -c 'ROSA_SKIP_BINFMT=1 CC="clang -O3 -Werror" go test ./...'
|
||||||
'')
|
'')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
46
container/binfmt.go
Normal file
46
container/binfmt.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"hakurei.app/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
// escapeBinfmt escapes magic/mask sequences in a [BinfmtEntry].
|
||||||
|
func escapeBinfmt(buf *strings.Builder, s string) string {
|
||||||
|
const lowerhex = "0123456789abcdef"
|
||||||
|
|
||||||
|
buf.Reset()
|
||||||
|
for _, c := range unsafe.Slice(unsafe.StringData(s), len(s)) {
|
||||||
|
switch c {
|
||||||
|
case 0, '\\', ':':
|
||||||
|
buf.WriteString(`\x`)
|
||||||
|
buf.WriteByte(lowerhex[c>>4])
|
||||||
|
buf.WriteByte(lowerhex[c&0xf])
|
||||||
|
|
||||||
|
default:
|
||||||
|
buf.WriteByte(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BinfmtEntry is an entry to be registered by the init process.
|
||||||
|
type BinfmtEntry struct {
|
||||||
|
// The offset of the magic/mask in the file, counted in bytes.
|
||||||
|
Offset byte
|
||||||
|
// The byte sequence binfmt_misc is matching for.
|
||||||
|
Magic string
|
||||||
|
// An (optional, defaults to all 0xff) mask.
|
||||||
|
Mask string
|
||||||
|
// The program that should be invoked with the binary as first argument.
|
||||||
|
Interpreter *check.Absolute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns whether e can be registered into the kernel.
|
||||||
|
func (e *BinfmtEntry) Valid() bool {
|
||||||
|
return e != nil &&
|
||||||
|
int(e.Offset)+max(len(e.Magic), len(e.Mask)) < 128 &&
|
||||||
|
e.Interpreter != nil && len(e.Interpreter.String()) < 128
|
||||||
|
}
|
||||||
62
container/binfmt_test.go
Normal file
62
container/binfmt_test.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/fhs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEscapeBinfmt(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
magic string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"packed DOS applications", "\x0eDEX", "\x0eDEX"},
|
||||||
|
|
||||||
|
{"riscv64 magic",
|
||||||
|
"\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00",
|
||||||
|
"\x7fELF\x02\x01\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\x02\\x00\xf3\\x00"},
|
||||||
|
{"riscv64 mask",
|
||||||
|
"\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff",
|
||||||
|
"\xff\xff\xff\xff\xff\xff\xff\\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff"},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
got := escapeBinfmt(new(strings.Builder), tc.magic)
|
||||||
|
if got != tc.want {
|
||||||
|
t.Errorf("escapeBinfmt: %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBinfmtEntry(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
e BinfmtEntry
|
||||||
|
valid bool
|
||||||
|
}{
|
||||||
|
{"zero", BinfmtEntry{}, false},
|
||||||
|
{"large offset", BinfmtEntry{Offset: 128}, false},
|
||||||
|
{"long magic", BinfmtEntry{Magic: strings.Repeat("\x00", 128)}, false},
|
||||||
|
{"long mask", BinfmtEntry{Mask: strings.Repeat("\x00", 128)}, false},
|
||||||
|
{"valid", BinfmtEntry{Interpreter: fhs.AbsRoot}, true},
|
||||||
|
}
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
if tc.e.Valid() != tc.valid {
|
||||||
|
t.Errorf("Valid: %v", !tc.valid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ const (
|
|||||||
CAP_SETPCAP = 0x8
|
CAP_SETPCAP = 0x8
|
||||||
CAP_NET_ADMIN = 0xc
|
CAP_NET_ADMIN = 0xc
|
||||||
CAP_DAC_OVERRIDE = 0x1
|
CAP_DAC_OVERRIDE = 0x1
|
||||||
|
CAP_SETFCAP = 0x1f
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -91,12 +91,20 @@ type (
|
|||||||
// Time to wait for processes lingering after the initial process terminates.
|
// Time to wait for processes lingering after the initial process terminates.
|
||||||
AdoptWaitDelay time.Duration
|
AdoptWaitDelay time.Duration
|
||||||
|
|
||||||
|
// Map uid/gid 0 in the init process. Requires [FstypeProc] attached to
|
||||||
|
// [fhs.Proc] in the container filesystem.
|
||||||
|
InitAsRoot bool
|
||||||
// Mapped Uid in user namespace.
|
// Mapped Uid in user namespace.
|
||||||
Uid int
|
Uid int
|
||||||
// Mapped Gid in user namespace.
|
// Mapped Gid in user namespace.
|
||||||
Gid int
|
Gid int
|
||||||
// Hostname value in UTS namespace.
|
// Hostname value in UTS namespace.
|
||||||
Hostname string
|
Hostname string
|
||||||
|
// Register binfmt_misc entries.
|
||||||
|
Binfmt []BinfmtEntry
|
||||||
|
// Alternative pathname to attach binfmt_misc filesystem. The zero value
|
||||||
|
// requires [FstypeProc] to be made available at [fhs.Proc].
|
||||||
|
BinfmtPath *check.Absolute
|
||||||
// Sequential container setup ops.
|
// Sequential container setup ops.
|
||||||
*Ops
|
*Ops
|
||||||
|
|
||||||
@@ -216,6 +224,9 @@ func (p *Container) Start() error {
|
|||||||
if p.cmd.Process != nil {
|
if p.cmd.Process != nil {
|
||||||
return errors.New("container: already started")
|
return errors.New("container: already started")
|
||||||
}
|
}
|
||||||
|
if !p.InitAsRoot && len(p.Binfmt) > 0 {
|
||||||
|
return errors.New("container: init as root required, but not enabled")
|
||||||
|
}
|
||||||
|
|
||||||
if err := ensureCloseOnExec(); err != nil {
|
if err := ensureCloseOnExec(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -286,6 +297,18 @@ func (p *Container) Start() error {
|
|||||||
if !p.HostNet {
|
if !p.HostNet {
|
||||||
p.cmd.SysProcAttr.Cloneflags |= CLONE_NEWNET
|
p.cmd.SysProcAttr.Cloneflags |= CLONE_NEWNET
|
||||||
}
|
}
|
||||||
|
if p.InitAsRoot {
|
||||||
|
p.cmd.SysProcAttr.AmbientCaps = append(p.cmd.SysProcAttr.AmbientCaps,
|
||||||
|
// mappings during init as root
|
||||||
|
CAP_SETFCAP,
|
||||||
|
)
|
||||||
|
|
||||||
|
if !p.SeccompDisable &&
|
||||||
|
len(p.SeccompRules) == 0 &&
|
||||||
|
p.SeccompPresets&std.PresetDenyNS != 0 {
|
||||||
|
return errors.New("container: as root requires late namespace creation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// place setup pipe before user supplied extra files, this is later restored by init
|
// place setup pipe before user supplied extra files, this is later restored by init
|
||||||
if r, w, err := os.Pipe(); err != nil {
|
if r, w, err := os.Pipe(); err != nil {
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
_ "unsafe" // for go:linkname
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"hakurei.app/check"
|
"hakurei.app/check"
|
||||||
"hakurei.app/command"
|
"hakurei.app/command"
|
||||||
@@ -408,39 +409,11 @@ var containerTestCases = []struct {
|
|||||||
func TestContainer(t *testing.T) {
|
func TestContainer(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
t.Run("cancel", testContainerCancel(nil, func(t *testing.T, c *container.Container) {
|
var suffix string
|
||||||
wantErr := context.Canceled
|
runTests:
|
||||||
wantExitCode := 0
|
|
||||||
if err := c.Wait(); !reflect.DeepEqual(err, wantErr) {
|
|
||||||
if m, ok := container.InternalMessageFromError(err); ok {
|
|
||||||
t.Error(m)
|
|
||||||
}
|
|
||||||
t.Errorf("Wait: error = %#v, want %#v", err, wantErr)
|
|
||||||
}
|
|
||||||
if ps := c.ProcessState(); ps == nil {
|
|
||||||
t.Errorf("ProcessState unexpectedly returned nil")
|
|
||||||
} else if code := ps.ExitCode(); code != wantExitCode {
|
|
||||||
t.Errorf("ExitCode: %d, want %d", code, wantExitCode)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
t.Run("forward", testContainerCancel(func(c *container.Container) {
|
|
||||||
c.ForwardCancel = true
|
|
||||||
}, func(t *testing.T, c *container.Container) {
|
|
||||||
var exitError *exec.ExitError
|
|
||||||
if err := c.Wait(); !errors.As(err, &exitError) {
|
|
||||||
if m, ok := container.InternalMessageFromError(err); ok {
|
|
||||||
t.Error(m)
|
|
||||||
}
|
|
||||||
t.Errorf("Wait: error = %v", err)
|
|
||||||
}
|
|
||||||
if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
|
|
||||||
t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
for i, tc := range containerTestCases {
|
for i, tc := range containerTestCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
_suffix := suffix
|
||||||
|
t.Run(tc.name+_suffix, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
wantOps, wantOpsCtx := tc.ops(t)
|
wantOps, wantOpsCtx := tc.ops(t)
|
||||||
@@ -464,6 +437,8 @@ func TestContainer(t *testing.T) {
|
|||||||
c.SeccompDisable = !tc.filter
|
c.SeccompDisable = !tc.filter
|
||||||
c.RetainSession = tc.session
|
c.RetainSession = tc.session
|
||||||
c.HostNet = tc.net
|
c.HostNet = tc.net
|
||||||
|
c.InitAsRoot = _suffix != ""
|
||||||
|
c.Env = append(c.Env, "HAKUREI_TEST_SUFFIX="+_suffix)
|
||||||
if info.CanDegrade {
|
if info.CanDegrade {
|
||||||
if _, err := landlock.GetABI(); err != nil {
|
if _, err := landlock.GetABI(); err != nil {
|
||||||
if !errors.Is(err, syscall.ENOSYS) {
|
if !errors.Is(err, syscall.ENOSYS) {
|
||||||
@@ -473,6 +448,9 @@ func TestContainer(t *testing.T) {
|
|||||||
t.Log("Landlock LSM is unavailable, enabling HostAbstract")
|
t.Log("Landlock LSM is unavailable, enabling HostAbstract")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if c.InitAsRoot {
|
||||||
|
c.SeccompPresets &= ^std.PresetDenyNS
|
||||||
|
}
|
||||||
|
|
||||||
c.
|
c.
|
||||||
Readonly(check.MustAbs(pathReadonly), 0755).
|
Readonly(check.MustAbs(pathReadonly), 0755).
|
||||||
@@ -541,6 +519,11 @@ func TestContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if suffix == "" {
|
||||||
|
suffix = " as root"
|
||||||
|
goto runTests
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ent(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoEntry {
|
func ent(root, target, vfsOptstr, fsType, source, fsOptstr string) *vfs.MountInfoEntry {
|
||||||
@@ -563,49 +546,118 @@ func hostnameFromTestCase(name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testContainerCancel(
|
func testContainerCancel(
|
||||||
|
t *testing.T,
|
||||||
containerExtra func(c *container.Container),
|
containerExtra func(c *container.Container),
|
||||||
waitCheck func(t *testing.T, c *container.Container),
|
waitCheck func(ps *os.ProcessState, waitErr error),
|
||||||
) func(t *testing.T) {
|
) {
|
||||||
return func(t *testing.T) {
|
ctx, cancel := context.WithCancel(t.Context())
|
||||||
t.Parallel()
|
|
||||||
ctx, cancel := context.WithCancel(t.Context())
|
|
||||||
|
|
||||||
c := helperNewContainer(ctx, "block")
|
c := helperNewContainer(ctx, "block")
|
||||||
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
||||||
if containerExtra != nil {
|
if containerExtra != nil {
|
||||||
containerExtra(c)
|
containerExtra(c)
|
||||||
}
|
|
||||||
|
|
||||||
ready := make(chan struct{})
|
|
||||||
if r, w, err := os.Pipe(); err != nil {
|
|
||||||
t.Fatalf("cannot pipe: %v", err)
|
|
||||||
} else {
|
|
||||||
c.ExtraFiles = append(c.ExtraFiles, w)
|
|
||||||
go func() {
|
|
||||||
defer close(ready)
|
|
||||||
if _, err = r.Read(make([]byte, 1)); err != nil {
|
|
||||||
panic(err.Error())
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Start(); err != nil {
|
|
||||||
if m, ok := container.InternalMessageFromError(err); ok {
|
|
||||||
t.Fatal(m)
|
|
||||||
} else {
|
|
||||||
t.Fatalf("cannot start container: %v", err)
|
|
||||||
}
|
|
||||||
} else if err = c.Serve(); err != nil {
|
|
||||||
if m, ok := container.InternalMessageFromError(err); ok {
|
|
||||||
t.Error(m)
|
|
||||||
} else {
|
|
||||||
t.Errorf("cannot serve setup params: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
<-ready
|
|
||||||
cancel()
|
|
||||||
waitCheck(t, c)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ready := make(chan struct{})
|
||||||
|
var waitErr error
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("cannot pipe: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ExtraFiles = append(c.ExtraFiles, w)
|
||||||
|
go func() {
|
||||||
|
defer close(ready)
|
||||||
|
if _, _err := r.Read(make([]byte, 1)); _err != nil {
|
||||||
|
panic(_err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = c.Start(); err != nil {
|
||||||
|
if m, ok := container.InternalMessageFromError(err); ok {
|
||||||
|
t.Fatal(m)
|
||||||
|
} else {
|
||||||
|
t.Fatalf("cannot start container: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer close(done)
|
||||||
|
waitErr = c.Wait()
|
||||||
|
_ = r.SetReadDeadline(time.Now())
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err = c.Serve(); err != nil {
|
||||||
|
if m, ok := container.InternalMessageFromError(err); ok {
|
||||||
|
t.Error(m)
|
||||||
|
} else {
|
||||||
|
t.Errorf("cannot serve setup params: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<-ready
|
||||||
|
cancel()
|
||||||
|
<-done
|
||||||
|
waitCheck(c.ProcessState(), waitErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForward(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := func(ps *os.ProcessState, waitErr error) {
|
||||||
|
var exitError *exec.ExitError
|
||||||
|
if !errors.As(waitErr, &exitError) {
|
||||||
|
if m, ok := container.InternalMessageFromError(waitErr); ok {
|
||||||
|
t.Error(m)
|
||||||
|
}
|
||||||
|
t.Errorf("Wait: error = %v", waitErr)
|
||||||
|
}
|
||||||
|
if code := exitError.ExitCode(); code != blockExitCodeInterrupt {
|
||||||
|
t.Errorf("ExitCode: %d, want %d", code, blockExitCodeInterrupt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("direct", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
testContainerCancel(t, func(c *container.Container) {
|
||||||
|
c.ForwardCancel = true
|
||||||
|
}, f)
|
||||||
|
})
|
||||||
|
t.Run("as root", func(t *testing.T) {
|
||||||
|
testContainerCancel(t, func(c *container.Container) {
|
||||||
|
c.ForwardCancel = true
|
||||||
|
c.InitAsRoot = true
|
||||||
|
c.Proc(fhs.AbsProc)
|
||||||
|
}, f)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCancel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
f := func(ps *os.ProcessState, waitErr error) {
|
||||||
|
wantErr := context.Canceled
|
||||||
|
if !reflect.DeepEqual(waitErr, wantErr) {
|
||||||
|
if m, ok := container.InternalMessageFromError(waitErr); ok {
|
||||||
|
t.Error(m)
|
||||||
|
}
|
||||||
|
t.Errorf("Wait: error = %#v, want %#v", waitErr, wantErr)
|
||||||
|
}
|
||||||
|
if ps == nil {
|
||||||
|
t.Errorf("ProcessState unexpectedly returned nil")
|
||||||
|
} else if code := ps.ExitCode(); code != 0 {
|
||||||
|
t.Errorf("ExitCode: %d, want %d", code, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.Run("direct", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
testContainerCancel(t, nil, f)
|
||||||
|
})
|
||||||
|
t.Run("as root", func(t *testing.T) {
|
||||||
|
testContainerCancel(t, func(c *container.Container) {
|
||||||
|
c.InitAsRoot = true
|
||||||
|
c.Proc(fhs.AbsProc)
|
||||||
|
}, f)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerString(t *testing.T) {
|
func TestContainerString(t *testing.T) {
|
||||||
@@ -641,6 +693,8 @@ func init() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
c.Command("container", command.UsageInternal, func(args []string) error {
|
c.Command("container", command.UsageInternal, func(args []string) error {
|
||||||
|
asRoot := os.Getenv("HAKUREI_TEST_SUFFIX") == " as root"
|
||||||
|
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return syscall.EINVAL
|
return syscall.EINVAL
|
||||||
}
|
}
|
||||||
@@ -658,6 +712,66 @@ func init() {
|
|||||||
return fmt.Errorf("gid: %d, want %d", gid, tc.gid)
|
return fmt.Errorf("gid: %d, want %d", gid, tc.gid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// no attack surface increase during as root due to no_new_privs
|
||||||
|
var wantBounding uintptr = 1
|
||||||
|
asRootNot := " not"
|
||||||
|
if !asRoot {
|
||||||
|
wantBounding = 0
|
||||||
|
asRootNot = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PR_CAP_AMBIENT = 0x2f
|
||||||
|
PR_CAP_AMBIENT_IS_SET = 0x1
|
||||||
|
)
|
||||||
|
for i := range container.LastCap(nil) + 1 {
|
||||||
|
r, _, errno := syscall.Syscall(
|
||||||
|
syscall.SYS_PRCTL,
|
||||||
|
PR_CAP_AMBIENT,
|
||||||
|
PR_CAP_AMBIENT_IS_SET,
|
||||||
|
i,
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return os.NewSyscallError("prctl", errno)
|
||||||
|
}
|
||||||
|
if r != 0 {
|
||||||
|
return fmt.Errorf("capability %d in ambient set", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, _, errno = syscall.Syscall(
|
||||||
|
syscall.SYS_PRCTL,
|
||||||
|
syscall.PR_CAPBSET_READ,
|
||||||
|
i,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
if errno != 0 {
|
||||||
|
return os.NewSyscallError("prctl", errno)
|
||||||
|
}
|
||||||
|
if r != wantBounding {
|
||||||
|
return fmt.Errorf("capability %d%s in bounding set", i, asRootNot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _LINUX_CAPABILITY_VERSION_3 = 0x20080522
|
||||||
|
var capData struct {
|
||||||
|
effective uint32
|
||||||
|
permitted uint32
|
||||||
|
inheritable uint32
|
||||||
|
}
|
||||||
|
if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&struct {
|
||||||
|
version uint32
|
||||||
|
pid int32
|
||||||
|
}{_LINUX_CAPABILITY_VERSION_3, 0})), uintptr(unsafe.Pointer(&capData)), 0); errno != 0 {
|
||||||
|
return os.NewSyscallError("capget", errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
if max(capData.effective, capData.permitted, capData.inheritable) != 0 {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"effective = %d, permitted = %d, inheritable = %d",
|
||||||
|
capData.effective, capData.permitted, capData.inheritable,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
wantHost := hostnameFromTestCase(tc.name)
|
wantHost := hostnameFromTestCase(tc.name)
|
||||||
if host, err := os.Hostname(); err != nil {
|
if host, err := os.Hostname(); err != nil {
|
||||||
return fmt.Errorf("cannot get hostname: %v", err)
|
return fmt.Errorf("cannot get hostname: %v", err)
|
||||||
@@ -775,7 +889,7 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
c.MustParse(os.Args[1:], func(err error) {
|
c.MustParse(os.Args[1:], func(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err.Error())
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
. "syscall"
|
. "syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"hakurei.app/check"
|
||||||
"hakurei.app/container/seccomp"
|
"hakurei.app/container/seccomp"
|
||||||
"hakurei.app/ext"
|
"hakurei.app/ext"
|
||||||
"hakurei.app/fhs"
|
"hakurei.app/fhs"
|
||||||
@@ -182,23 +184,33 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uid, gid := param.Uid, param.Gid
|
||||||
|
if param.InitAsRoot {
|
||||||
|
uid, gid = 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
// write uid/gid map here so parent does not need to set dumpable
|
// write uid/gid map here so parent does not need to set dumpable
|
||||||
if err := k.setDumpable(ext.SUID_DUMP_USER); err != nil {
|
if err := k.setDumpable(ext.SUID_DUMP_USER); err != nil {
|
||||||
k.fatalf(msg, "cannot set SUID_DUMP_USER: %v", err)
|
k.fatalf(msg, "cannot set SUID_DUMP_USER: %v", err)
|
||||||
}
|
}
|
||||||
if err := k.writeFile(fhs.Proc+"self/uid_map",
|
if err := k.writeFile(
|
||||||
append([]byte{}, strconv.Itoa(param.Uid)+" "+strconv.Itoa(param.HostUid)+" 1\n"...),
|
fhs.Proc+"self/uid_map",
|
||||||
0); err != nil {
|
[]byte(strconv.Itoa(uid)+" "+strconv.Itoa(param.HostUid)+" 1\n"),
|
||||||
|
0,
|
||||||
|
); err != nil {
|
||||||
k.fatalf(msg, "%v", err)
|
k.fatalf(msg, "%v", err)
|
||||||
}
|
}
|
||||||
if err := k.writeFile(fhs.Proc+"self/setgroups",
|
if err := k.writeFile(
|
||||||
|
fhs.Proc+"self/setgroups",
|
||||||
[]byte("deny\n"),
|
[]byte("deny\n"),
|
||||||
0); err != nil && !os.IsNotExist(err) {
|
0,
|
||||||
|
); err != nil && !os.IsNotExist(err) {
|
||||||
k.fatalf(msg, "%v", err)
|
k.fatalf(msg, "%v", err)
|
||||||
}
|
}
|
||||||
if err := k.writeFile(fhs.Proc+"self/gid_map",
|
if err := k.writeFile(fhs.Proc+"self/gid_map",
|
||||||
append([]byte{}, strconv.Itoa(param.Gid)+" "+strconv.Itoa(param.HostGid)+" 1\n"...),
|
[]byte(strconv.Itoa(gid)+" "+strconv.Itoa(param.HostGid)+" 1\n"),
|
||||||
0); err != nil {
|
0,
|
||||||
|
); err != nil {
|
||||||
k.fatalf(msg, "%v", err)
|
k.fatalf(msg, "%v", err)
|
||||||
}
|
}
|
||||||
if err := k.setDumpable(ext.SUID_DUMP_DISABLE); err != nil {
|
if err := k.setDumpable(ext.SUID_DUMP_DISABLE); err != nil {
|
||||||
@@ -230,6 +242,16 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
k.fatalf(msg, "cannot enter intermediate host path: %v", err)
|
k.fatalf(msg, "cannot enter intermediate host path: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(param.Binfmt) > 0 {
|
||||||
|
for i, e := range param.Binfmt {
|
||||||
|
if pathname, err := k.evalSymlinks(e.Interpreter.String()); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
} else if param.Binfmt[i].Interpreter, err = check.NewAbs(pathname); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* early is called right before pivot_root into intermediate root;
|
/* early is called right before pivot_root into intermediate root;
|
||||||
this step is mostly for gathering information that would otherwise be
|
this step is mostly for gathering information that would otherwise be
|
||||||
difficult to obtain via library functions after pivot_root, and
|
difficult to obtain via library functions after pivot_root, and
|
||||||
@@ -285,6 +307,48 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(param.Binfmt) > 0 {
|
||||||
|
const interpreter = "/interpreter"
|
||||||
|
|
||||||
|
if param.BinfmtPath == nil {
|
||||||
|
param.BinfmtPath = fhs.AbsProcSys.Append("fs/binfmt_misc")
|
||||||
|
}
|
||||||
|
binfmt := sysrootPath + param.BinfmtPath.String()
|
||||||
|
if err := k.mkdirAll(binfmt, 0); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
}
|
||||||
|
if err := k.mount(
|
||||||
|
SourceBinfmtMisc,
|
||||||
|
binfmt,
|
||||||
|
FstypeBinfmtMisc,
|
||||||
|
MS_NOSUID|MS_NOEXEC|MS_NODEV,
|
||||||
|
zeroString,
|
||||||
|
); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
buf.Grow(1920)
|
||||||
|
|
||||||
|
register := binfmt + "/register"
|
||||||
|
for i, e := range param.Binfmt {
|
||||||
|
if err := k.symlink(hostPath+e.Interpreter.String(), interpreter); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
} else if err = k.writeFile(register, []byte(":"+
|
||||||
|
strconv.Itoa(i)+":"+
|
||||||
|
"M:"+
|
||||||
|
strconv.Itoa(int(e.Offset))+":"+
|
||||||
|
escapeBinfmt(&buf, e.Magic)+":"+
|
||||||
|
escapeBinfmt(&buf, e.Mask)+":"+
|
||||||
|
interpreter+":"+
|
||||||
|
"F"), 0); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
} else if err = k.remove(interpreter); err != nil {
|
||||||
|
k.fatal(msg, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// setup requiring host root complete at this point
|
// setup requiring host root complete at this point
|
||||||
if err := k.mount(hostDir, hostDir, zeroString, MS_SILENT|MS_REC|MS_PRIVATE, zeroString); err != nil {
|
if err := k.mount(hostDir, hostDir, zeroString, MS_SILENT|MS_REC|MS_PRIVATE, zeroString); err != nil {
|
||||||
k.fatalf(msg, "cannot make host root rprivate: %v", optionalErrorUnwrap(err))
|
k.fatalf(msg, "cannot make host root rprivate: %v", optionalErrorUnwrap(err))
|
||||||
@@ -323,11 +387,19 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var keepCaps []uintptr
|
||||||
|
if param.Privileged {
|
||||||
|
keepCaps = append(keepCaps, CAP_SYS_ADMIN, CAP_SETPCAP)
|
||||||
|
}
|
||||||
|
if param.InitAsRoot {
|
||||||
|
keepCaps = append(keepCaps, CAP_SETFCAP)
|
||||||
|
}
|
||||||
|
|
||||||
if err := k.capAmbientClearAll(); err != nil {
|
if err := k.capAmbientClearAll(); err != nil {
|
||||||
k.fatalf(msg, "cannot clear the ambient capability set: %v", err)
|
k.fatalf(msg, "cannot clear the ambient capability set: %v", err)
|
||||||
}
|
}
|
||||||
for i := uintptr(0); i <= lastcap; i++ {
|
for i := range lastcap + 1 {
|
||||||
if param.Privileged && i == CAP_SYS_ADMIN {
|
if slices.Contains(keepCaps, i) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := k.capBoundingSetDrop(i); err != nil {
|
if err := k.capBoundingSetDrop(i); err != nil {
|
||||||
@@ -336,20 +408,23 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var keep [2]uint32
|
var keep [2]uint32
|
||||||
if param.Privileged {
|
for _, c := range keepCaps {
|
||||||
keep[capToIndex(CAP_SYS_ADMIN)] |= capToMask(CAP_SYS_ADMIN)
|
keep[capToIndex(c)] |= capToMask(c)
|
||||||
|
|
||||||
if err := k.capAmbientRaise(CAP_SYS_ADMIN); err != nil {
|
|
||||||
k.fatalf(msg, "cannot raise CAP_SYS_ADMIN: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := k.capset(
|
if err := k.capset(
|
||||||
&capHeader{_LINUX_CAPABILITY_VERSION_3, 0},
|
&capHeader{_LINUX_CAPABILITY_VERSION_3, 0},
|
||||||
&[2]capData{{0, keep[0], keep[0]}, {0, keep[1], keep[1]}},
|
&[2]capData{{keep[0], keep[0], keep[0]}, {keep[1], keep[1], keep[1]}},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
k.fatalf(msg, "cannot capset: %v", err)
|
k.fatalf(msg, "cannot capset: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, c := range keepCaps {
|
||||||
|
if err := k.capAmbientRaise(c); err != nil {
|
||||||
|
k.fatalf(msg, "cannot raise %#x: %v", c, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !param.SeccompDisable {
|
if !param.SeccompDisable {
|
||||||
rules := param.SeccompRules
|
rules := param.SeccompRules
|
||||||
if len(rules) == 0 { // non-empty rules slice always overrides presets
|
if len(rules) == 0 { // non-empty rules slice always overrides presets
|
||||||
@@ -474,6 +549,14 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
|||||||
cmd.ExtraFiles = extraFiles
|
cmd.ExtraFiles = extraFiles
|
||||||
cmd.Dir = param.Dir.String()
|
cmd.Dir = param.Dir.String()
|
||||||
|
|
||||||
|
if param.InitAsRoot {
|
||||||
|
cmd.SysProcAttr = &SysProcAttr{
|
||||||
|
Cloneflags: CLONE_NEWUSER,
|
||||||
|
UidMappings: []SysProcIDMap{{ContainerID: param.Uid, HostID: 0, Size: 1}},
|
||||||
|
GidMappings: []SysProcIDMap{{ContainerID: param.Gid, HostID: 0, Size: 1}},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
msg.Verbosef("starting initial process %s", param.Path)
|
msg.Verbosef("starting initial process %s", param.Path)
|
||||||
if err := k.start(cmd); err != nil {
|
if err := k.start(cmd); err != nil {
|
||||||
k.fatalf(msg, "%v", err)
|
k.fatalf(msg, "%v", err)
|
||||||
|
|||||||
@@ -1624,7 +1624,6 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x8)}, nil, nil),
|
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||||
@@ -1656,8 +1655,9 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
||||||
|
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0x200100, 0x200100, 0x200100}, {0, 0, 0}}}, nil, nil),
|
||||||
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, stub.UniqueError(19)),
|
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, stub.UniqueError(19)),
|
||||||
call("fatalf", stub.ExpectArgs{"cannot raise CAP_SYS_ADMIN: %v", []any{stub.UniqueError(19)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot raise %#x: %v", []any{uintptr(0x15), stub.UniqueError(19)}}, nil, nil),
|
||||||
},
|
},
|
||||||
}, nil},
|
}, nil},
|
||||||
|
|
||||||
@@ -1731,7 +1731,6 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x8)}, nil, nil),
|
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||||
@@ -1763,8 +1762,7 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
||||||
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
|
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0x200100, 0x200100, 0x200100}, {0, 0, 0}}}, nil, stub.UniqueError(17)),
|
||||||
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, stub.UniqueError(17)),
|
|
||||||
call("fatalf", stub.ExpectArgs{"cannot capset: %v", []any{stub.UniqueError(17)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot capset: %v", []any{stub.UniqueError(17)}}, nil, nil),
|
||||||
},
|
},
|
||||||
}, nil},
|
}, nil},
|
||||||
@@ -1839,7 +1837,6 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x8)}, nil, nil),
|
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||||
@@ -1871,8 +1868,9 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
||||||
|
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0x200100, 0x200100, 0x200100}, {0, 0, 0}}}, nil, nil),
|
||||||
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
|
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
|
||||||
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil),
|
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x8)}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{std.FilterPreset(0xf)}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{std.FilterPreset(0xf)}}, nil, nil),
|
||||||
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, stub.UniqueError(15)),
|
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, stub.UniqueError(15)),
|
||||||
call("fatalf", stub.ExpectArgs{"cannot load syscall filter: %v", []any{stub.UniqueError(15)}}, nil, nil),
|
call("fatalf", stub.ExpectArgs{"cannot load syscall filter: %v", []any{stub.UniqueError(15)}}, nil, nil),
|
||||||
@@ -2699,7 +2697,6 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x7)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x8)}, nil, nil),
|
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x9)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xa)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||||
@@ -2731,8 +2728,9 @@ func TestInitEntrypoint(t *testing.T) {
|
|||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, nil, nil),
|
||||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x28)}, nil, nil),
|
||||||
|
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0x200100, 0x200100, 0x200100}, {0, 0, 0}}}, nil, nil),
|
||||||
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
|
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x15)}, nil, nil),
|
||||||
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, nil),
|
call("capAmbientRaise", stub.ExpectArgs{uintptr(0x8)}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{std.FilterPreset(0xf)}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"resolving presets %#x", []any{std.FilterPreset(0xf)}}, nil, nil),
|
||||||
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil),
|
call("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil),
|
||||||
call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil),
|
call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil),
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ const (
|
|||||||
// SourceMqueue is used when mounting mqueue.
|
// SourceMqueue is used when mounting mqueue.
|
||||||
// Note that any source value is allowed when fstype is [FstypeMqueue].
|
// Note that any source value is allowed when fstype is [FstypeMqueue].
|
||||||
SourceMqueue = "mqueue"
|
SourceMqueue = "mqueue"
|
||||||
|
// SourceBinfmtMisc is used when mounting binfmt_misc.
|
||||||
|
// Note that any source value is allowed when fstype is [SourceBinfmtMisc].
|
||||||
|
SourceBinfmtMisc = "binfmt_misc"
|
||||||
// SourceOverlay is used when mounting overlay.
|
// SourceOverlay is used when mounting overlay.
|
||||||
// Note that any source value is allowed when fstype is [FstypeOverlay].
|
// Note that any source value is allowed when fstype is [FstypeOverlay].
|
||||||
SourceOverlay = "overlay"
|
SourceOverlay = "overlay"
|
||||||
@@ -70,6 +73,9 @@ const (
|
|||||||
// FstypeMqueue represents the mqueue pseudo-filesystem.
|
// FstypeMqueue represents the mqueue pseudo-filesystem.
|
||||||
// This filesystem type is usually mounted on /dev/mqueue.
|
// This filesystem type is usually mounted on /dev/mqueue.
|
||||||
FstypeMqueue = "mqueue"
|
FstypeMqueue = "mqueue"
|
||||||
|
// FstypeBinfmtMisc represents the binfmt_misc pseudo-filesystem.
|
||||||
|
// This filesystem type is usually mounted on /proc/sys/fs/binfmt_misc.
|
||||||
|
FstypeBinfmtMisc = "binfmt_misc"
|
||||||
// FstypeOverlay represents the overlay pseudo-filesystem.
|
// FstypeOverlay represents the overlay pseudo-filesystem.
|
||||||
// This filesystem type can be mounted anywhere in the container filesystem.
|
// This filesystem type can be mounted anywhere in the container filesystem.
|
||||||
FstypeOverlay = "overlay"
|
FstypeOverlay = "overlay"
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ var (
|
|||||||
AbsDevShm = unsafeAbs(DevShm)
|
AbsDevShm = unsafeAbs(DevShm)
|
||||||
// AbsProc is [Proc] as [check.Absolute].
|
// AbsProc is [Proc] as [check.Absolute].
|
||||||
AbsProc = unsafeAbs(Proc)
|
AbsProc = unsafeAbs(Proc)
|
||||||
|
// AbsProcSys is [ProcSys] as [check.Absolute].
|
||||||
|
AbsProcSys = unsafeAbs(ProcSys)
|
||||||
// AbsProcSelfExe is [ProcSelfExe] as [check.Absolute].
|
// AbsProcSelfExe is [ProcSelfExe] as [check.Absolute].
|
||||||
AbsProcSelfExe = unsafeAbs(ProcSelfExe)
|
AbsProcSelfExe = unsafeAbs(ProcSelfExe)
|
||||||
// AbsSys is [Sys] as [check.Absolute].
|
// AbsSys is [Sys] as [check.Absolute].
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
"unique"
|
"unique"
|
||||||
@@ -94,6 +96,32 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath {
|
|||||||
return ExecPath{check.MustAbs(pathname), a, writable}
|
return ExecPath{check.MustAbs(pathname), a, writable}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
binfmt map[string]container.BinfmtEntry
|
||||||
|
binfmtMu sync.RWMutex
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterArch arranges for [KindExec] and [KindExecNet] to support a new
|
||||||
|
// architecture via a binfmt_misc entry. Each architecture must be registered
|
||||||
|
// at most once.
|
||||||
|
func RegisterArch(arch string, e container.BinfmtEntry) {
|
||||||
|
if arch == "" {
|
||||||
|
panic(UnsupportedArchError(arch))
|
||||||
|
}
|
||||||
|
|
||||||
|
binfmtMu.Lock()
|
||||||
|
defer binfmtMu.Unlock()
|
||||||
|
|
||||||
|
if binfmt == nil {
|
||||||
|
binfmt = make(map[string]container.BinfmtEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := binfmt[arch]; ok {
|
||||||
|
panic("attempting to register " + strconv.Quote(arch) + " twice")
|
||||||
|
}
|
||||||
|
binfmt[arch] = e
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ExecTimeoutDefault replaces out of range [NewExec] timeout values.
|
// ExecTimeoutDefault replaces out of range [NewExec] timeout values.
|
||||||
ExecTimeoutDefault = 15 * time.Minute
|
ExecTimeoutDefault = 15 * time.Minute
|
||||||
@@ -110,6 +138,8 @@ type execArtifact struct {
|
|||||||
// Caller-supplied user-facing reporting name, guaranteed to be nonzero
|
// Caller-supplied user-facing reporting name, guaranteed to be nonzero
|
||||||
// during initialisation.
|
// during initialisation.
|
||||||
name string
|
name string
|
||||||
|
// Target architecture.
|
||||||
|
arch string
|
||||||
// Caller-supplied inner mount points.
|
// Caller-supplied inner mount points.
|
||||||
paths []ExecPath
|
paths []ExecPath
|
||||||
|
|
||||||
@@ -178,7 +208,7 @@ func (a *execNetArtifact) Cure(f *FContext) error {
|
|||||||
// container and does not affect curing outcome. Because of this, it is omitted
|
// container and does not affect curing outcome. Because of this, it is omitted
|
||||||
// from parameter data for computing identifier.
|
// from parameter data for computing identifier.
|
||||||
func NewExec(
|
func NewExec(
|
||||||
name string,
|
name, arch string,
|
||||||
checksum *Checksum,
|
checksum *Checksum,
|
||||||
timeout time.Duration,
|
timeout time.Duration,
|
||||||
exclusive bool,
|
exclusive bool,
|
||||||
@@ -193,13 +223,16 @@ func NewExec(
|
|||||||
if name == "" {
|
if name == "" {
|
||||||
name = "exec-" + filepath.Base(pathname.String())
|
name = "exec-" + filepath.Base(pathname.String())
|
||||||
}
|
}
|
||||||
|
if arch == "" {
|
||||||
|
arch = runtime.GOARCH
|
||||||
|
}
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = ExecTimeoutDefault
|
timeout = ExecTimeoutDefault
|
||||||
}
|
}
|
||||||
if timeout > ExecTimeoutMax {
|
if timeout > ExecTimeoutMax {
|
||||||
timeout = ExecTimeoutMax
|
timeout = ExecTimeoutMax
|
||||||
}
|
}
|
||||||
a := execArtifact{name, paths, dir, env, pathname, args, timeout, exclusive}
|
a := execArtifact{name, arch, paths, dir, env, pathname, args, timeout, exclusive}
|
||||||
if checksum == nil {
|
if checksum == nil {
|
||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
@@ -211,6 +244,7 @@ func (*execArtifact) Kind() Kind { return KindExec }
|
|||||||
|
|
||||||
// Params writes paths, executable pathname and args.
|
// Params writes paths, executable pathname and args.
|
||||||
func (a *execArtifact) Params(ctx *IContext) {
|
func (a *execArtifact) Params(ctx *IContext) {
|
||||||
|
ctx.WriteString(a.arch)
|
||||||
ctx.WriteString(a.name)
|
ctx.WriteString(a.name)
|
||||||
|
|
||||||
ctx.WriteUint32(uint32(len(a.paths)))
|
ctx.WriteUint32(uint32(len(a.paths)))
|
||||||
@@ -257,11 +291,26 @@ func (a *execArtifact) Params(ctx *IContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnsupportedArchError describes an unsupported or invalid architecture.
|
||||||
|
type UnsupportedArchError string
|
||||||
|
|
||||||
|
func (e UnsupportedArchError) Error() string {
|
||||||
|
if e == "" {
|
||||||
|
return "invalid architecture name"
|
||||||
|
}
|
||||||
|
return "unsupported architecture " + string(e)
|
||||||
|
}
|
||||||
|
|
||||||
// readExecArtifact interprets IR values and returns the address of execArtifact
|
// readExecArtifact interprets IR values and returns the address of execArtifact
|
||||||
// or execNetArtifact.
|
// or execNetArtifact.
|
||||||
func readExecArtifact(r *IRReader, net bool) Artifact {
|
func readExecArtifact(r *IRReader, net bool) Artifact {
|
||||||
r.DiscardAll()
|
r.DiscardAll()
|
||||||
|
|
||||||
|
arch := r.ReadString()
|
||||||
|
if arch == "" {
|
||||||
|
panic(UnsupportedArchError(arch))
|
||||||
|
}
|
||||||
|
|
||||||
name := r.ReadString()
|
name := r.ReadString()
|
||||||
|
|
||||||
sz := r.ReadUint32()
|
sz := r.ReadUint32()
|
||||||
@@ -327,7 +376,7 @@ func readExecArtifact(r *IRReader, net bool) Artifact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return NewExec(
|
return NewExec(
|
||||||
name, checksumP, timeout, exclusive, dir, env, pathname, args, paths...,
|
name, arch, checksumP, timeout, exclusive, dir, env, pathname, args, paths...,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,6 +491,17 @@ func (a *execArtifact) makeContainer(
|
|||||||
z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)})
|
z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)})
|
||||||
z.Grow(len(a.paths) + 4)
|
z.Grow(len(a.paths) + 4)
|
||||||
|
|
||||||
|
if a.arch != runtime.GOARCH {
|
||||||
|
binfmtMu.RLock()
|
||||||
|
e, ok := binfmt[a.arch]
|
||||||
|
binfmtMu.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
return nil, UnsupportedArchError(a.arch)
|
||||||
|
}
|
||||||
|
z.Binfmt = []container.BinfmtEntry{e}
|
||||||
|
z.InitAsRoot = true
|
||||||
|
}
|
||||||
|
|
||||||
for i, b := range a.paths {
|
for i, b := range a.paths {
|
||||||
if i == overlayWorkIndex {
|
if i == overlayWorkIndex {
|
||||||
if err = os.MkdirAll(work.String(), 0700); err != nil {
|
if err = os.MkdirAll(work.String(), 0700); err != nil {
|
||||||
@@ -631,12 +691,6 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
|
|||||||
_ = stdout.Close()
|
_ = stdout.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer func() {
|
|
||||||
if err != nil && !errors.As(err, new(*exec.ExitError)) {
|
|
||||||
_ = stdout.Close()
|
|
||||||
_ = stderr.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
brStdout, brStderr := f.cache.getReader(stdout), f.cache.getReader(stderr)
|
brStdout, brStderr := f.cache.getReader(stdout), f.cache.getReader(stderr)
|
||||||
stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
|
stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
|
||||||
@@ -651,6 +705,11 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
|
|||||||
io.TeeReader(brStderr, status),
|
io.TeeReader(brStderr, status),
|
||||||
)
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
if err != nil && !errors.As(err, new(*exec.ExitError)) {
|
||||||
|
_ = stdout.Close()
|
||||||
|
_ = stderr.Close()
|
||||||
|
}
|
||||||
|
|
||||||
<-stdoutDone
|
<-stdoutDone
|
||||||
<-stderrDone
|
<-stderrDone
|
||||||
f.cache.putReader(brStdout)
|
f.cache.putReader(brStdout)
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
"unique"
|
"unique"
|
||||||
|
|
||||||
"hakurei.app/check"
|
"hakurei.app/check"
|
||||||
|
"hakurei.app/container"
|
||||||
"hakurei.app/hst"
|
"hakurei.app/hst"
|
||||||
|
"hakurei.app/internal/info"
|
||||||
"hakurei.app/internal/pkg"
|
"hakurei.app/internal/pkg"
|
||||||
"hakurei.app/internal/stub"
|
"hakurei.app/internal/stub"
|
||||||
|
|
||||||
@@ -27,6 +30,17 @@ import (
|
|||||||
//go:embed internal/testtool/testtool
|
//go:embed internal/testtool/testtool
|
||||||
var testtoolBin []byte
|
var testtoolBin []byte
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pathname, err := filepath.Abs("internal/testtool/testtool")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pkg.RegisterArch("cafe", container.BinfmtEntry{
|
||||||
|
Magic: expected.Magic,
|
||||||
|
Interpreter: check.MustAbs(pathname),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestExec(t *testing.T) {
|
func TestExec(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -43,7 +57,7 @@ func TestExec(t *testing.T) {
|
|||||||
|
|
||||||
cureMany(t, c, []cureStep{
|
cureMany(t, c, []cureStep{
|
||||||
{"container", pkg.NewExec(
|
{"container", pkg.NewExec(
|
||||||
"exec-offline", nil, 0, false,
|
"exec-offline", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1"},
|
[]string{"HAKUREI_TEST=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -66,7 +80,7 @@ func TestExec(t *testing.T) {
|
|||||||
), ignorePathname, wantOffline, nil},
|
), ignorePathname, wantOffline, nil},
|
||||||
|
|
||||||
{"error passthrough", pkg.NewExec(
|
{"error passthrough", pkg.NewExec(
|
||||||
"", nil, 0, true,
|
"", "", nil, 0, true,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1"},
|
[]string{"HAKUREI_TEST=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -89,7 +103,7 @@ func TestExec(t *testing.T) {
|
|||||||
}},
|
}},
|
||||||
|
|
||||||
{"invalid paths", pkg.NewExec(
|
{"invalid paths", pkg.NewExec(
|
||||||
"", nil, 0, false,
|
"", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1"},
|
[]string{"HAKUREI_TEST=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -102,7 +116,7 @@ func TestExec(t *testing.T) {
|
|||||||
// check init failure passthrough
|
// check init failure passthrough
|
||||||
var exitError *exec.ExitError
|
var exitError *exec.ExitError
|
||||||
if _, _, err := c.Cure(pkg.NewExec(
|
if _, _, err := c.Cure(pkg.NewExec(
|
||||||
"", nil, 0, false,
|
"", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
nil,
|
nil,
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -141,7 +155,7 @@ func TestExec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cureMany(t, c, []cureStep{
|
cureMany(t, c, []cureStep{
|
||||||
{"container", pkg.NewExec(
|
{"container", pkg.NewExec(
|
||||||
"exec-net", new(wantNet.hash()), 0, false,
|
"exec-net", "", new(wantNet.hash()), 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1"},
|
[]string{"HAKUREI_TEST=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -188,7 +202,7 @@ func TestExec(t *testing.T) {
|
|||||||
|
|
||||||
cureMany(t, c, []cureStep{
|
cureMany(t, c, []cureStep{
|
||||||
{"container", pkg.NewExec(
|
{"container", pkg.NewExec(
|
||||||
"exec-overlay-root", nil, 0, false,
|
"exec-overlay-root", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -227,7 +241,7 @@ func TestExec(t *testing.T) {
|
|||||||
|
|
||||||
cureMany(t, c, []cureStep{
|
cureMany(t, c, []cureStep{
|
||||||
{"container", pkg.NewExec(
|
{"container", pkg.NewExec(
|
||||||
"exec-overlay-work", nil, 0, false,
|
"exec-overlay-work", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||||
check.MustAbs("/work/bin/testtool"),
|
check.MustAbs("/work/bin/testtool"),
|
||||||
@@ -271,7 +285,7 @@ func TestExec(t *testing.T) {
|
|||||||
|
|
||||||
cureMany(t, c, []cureStep{
|
cureMany(t, c, []cureStep{
|
||||||
{"container", pkg.NewExec(
|
{"container", pkg.NewExec(
|
||||||
"exec-multiple-layers", nil, 0, false,
|
"exec-multiple-layers", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -342,7 +356,7 @@ func TestExec(t *testing.T) {
|
|||||||
|
|
||||||
cureMany(t, c, []cureStep{
|
cureMany(t, c, []cureStep{
|
||||||
{"container", pkg.NewExec(
|
{"container", pkg.NewExec(
|
||||||
"exec-layer-promotion", nil, 0, true,
|
"exec-layer-promotion", "", nil, 0, true,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -382,6 +396,69 @@ func TestExec(t *testing.T) {
|
|||||||
"temp": {Mode: fs.ModeDir | 0700},
|
"temp": {Mode: fs.ModeDir | 0700},
|
||||||
"work": {Mode: fs.ModeDir | 0700},
|
"work": {Mode: fs.ModeDir | 0700},
|
||||||
}},
|
}},
|
||||||
|
|
||||||
|
{"binfmt", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||||
|
if info.CanDegrade && os.Getenv("ROSA_SKIP_BINFMT") != "" {
|
||||||
|
t.Skip("binfmt_misc test explicitly skipped")
|
||||||
|
}
|
||||||
|
|
||||||
|
cureMany(t, c, []cureStep{
|
||||||
|
{"container", pkg.NewExec(
|
||||||
|
"exec-binfmt", "cafe", nil, 0, true,
|
||||||
|
pkg.AbsWork,
|
||||||
|
[]string{"HAKUREI_TEST=1", "HAKUREI_BINFMT=1"},
|
||||||
|
check.MustAbs("/opt/bin/sample"),
|
||||||
|
[]string{"sample"},
|
||||||
|
|
||||||
|
pkg.MustPath("/", true, &stubArtifact{
|
||||||
|
kind: pkg.KindTar,
|
||||||
|
params: []byte("empty directory"),
|
||||||
|
cure: func(t *pkg.TContext) error {
|
||||||
|
return os.MkdirAll(t.GetWorkDir().String(), 0700)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
pkg.MustPath("/opt", false, overrideIdent{pkg.ID{0xfe, 0xff}, &stubArtifact{
|
||||||
|
kind: pkg.KindTar,
|
||||||
|
cure: func(t *pkg.TContext) error {
|
||||||
|
work := t.GetWorkDir()
|
||||||
|
if err := os.MkdirAll(
|
||||||
|
work.Append("bin").String(),
|
||||||
|
0700,
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(t.GetWorkDir().Append(
|
||||||
|
"bin",
|
||||||
|
"sample",
|
||||||
|
).String(), []byte(expected.Full), 0500)
|
||||||
|
},
|
||||||
|
}}),
|
||||||
|
), ignorePathname, expectsFS{
|
||||||
|
".": {Mode: fs.ModeDir | 0500},
|
||||||
|
|
||||||
|
"check": {Mode: 0400, Data: []byte("binfmt")},
|
||||||
|
}, nil},
|
||||||
|
})
|
||||||
|
}, expectsFS{
|
||||||
|
".": {Mode: fs.ModeDir | 0700},
|
||||||
|
|
||||||
|
"checksum": {Mode: fs.ModeDir | 0700},
|
||||||
|
"checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV": {Mode: fs.ModeDir | 0500},
|
||||||
|
"checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV/bin": {Mode: fs.ModeDir | 0700},
|
||||||
|
"checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV/bin/sample": {Mode: 0500, Data: []byte("\xca\xfe\xba\xbe\xfd\xfd:3")},
|
||||||
|
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||||
|
"checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3": {Mode: fs.ModeDir | 0500},
|
||||||
|
"checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3/check": {Mode: 0400, Data: []byte("binfmt")},
|
||||||
|
|
||||||
|
"identifier": {Mode: fs.ModeDir | 0700},
|
||||||
|
"identifier/6VQTJ1lI5BmVuI1YFYJ8ClO3MRORvTTrcWFDcUU-l5Ga8EofxCxGlSTYN-u8dKj_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/UnDo4B5KneEUY5b4vRUk_y9MWgkWuw2N8f8a2XayO686xXur-aZmX2-7n_8tKMe3")},
|
||||||
|
"identifier/_v8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/5aevg3YpDxjqQZ-pdvXK7YqgkL5JKqcoStYQxeD96kuYar6K2mRQWMHib6NQRnpV")},
|
||||||
|
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||||
|
|
||||||
|
"temp": {Mode: fs.ModeDir | 0700},
|
||||||
|
"work": {Mode: fs.ModeDir | 0700},
|
||||||
|
}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
// Package expected contains expected identifiers for exec artifact tests.
|
|
||||||
package expected
|
|
||||||
9
internal/pkg/internal/testtool/expected/expected.go
Normal file
9
internal/pkg/internal/testtool/expected/expected.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// Package expected contains data shared between test helper and test harness.
|
||||||
|
package expected
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Magic are magic bytes in the binfmt test case.
|
||||||
|
Magic = "\xca\xfe\xba\xbe\xfd\xfd"
|
||||||
|
// Full is the full content of the binfmt test case executable.
|
||||||
|
Full = Magic + ":3"
|
||||||
|
)
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package expected
|
package expected
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Offline = "dztPS6jRjiZtCF4_p8AzfnxGp6obkhrgFVsxdodbKWUoAEVtDz3MykepJB4kI_ks"
|
Offline = "oe7Uv1u5BwxcuX3HLQzZRg1Q5oetJo6jWiKGMOeqLiqBkaVgyKzvx82N81_IzUAz"
|
||||||
OvlRoot = "RdMA-mubnrHuu3Ky1wWyxauSYCO0ZH_zCPUj3uDHqkfwv5sGcByoF_g5PjlGiClb"
|
OvlRoot = "NacZGXwuRkTvcHaG08a22ujJ8qCWN0RSoFlRSR5FSt0ZcBbJ28FRvkYsHEtX7G8i"
|
||||||
Layers = "p1t_drXr34i-jZNuxDMLaMOdL6tZvQqhavNafGynGqxOZoXAUTSn7kqNh3Ovv3DT"
|
Layers = "WBJDrATtX6rIE5yAu8ePX3WmDF0Tt9kFiue0m3cRnyRoVx1my8a67fh3CAW486oP"
|
||||||
Net = "G8qPxD9puvvoOVV7lrT80eyDeIl3G_CCFoKw12c8mCjMdG1zF7NEPkwYpNubClK3"
|
Net = "CmYtj2sNB3LHtqiDuck_Lz3MjLLIiwyP8N4NDitQ1Icvv__LVP9p8tm-sHeQaKKp"
|
||||||
Promote = "xXTIYcXmgJWNLC91c417RRrNM9cjELwEZHpGvf8Fk_GNP5agRJp_SicD0w9aMeLJ"
|
Promote = "TX3eCloaQFkV-SZIH6Jg6E5WKH--rcXY1P0jnZKmLFKWrNqnOzd4G9eIBh6i5ywN"
|
||||||
Work = "5hlaukCirnXE4W_RSLJFOZN47Z5RiHnacXzdFp_70cLgiJUGR6cSb_HaFftkzi0-"
|
Work = "OuNiLSC68pZhAOr1YQ4WbV1tzASA0nxLEBcK7lO7MqxDY_j8dmP_C612RTuF23Lu"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,6 +24,19 @@ func main() {
|
|||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
log.SetPrefix("testtool: ")
|
log.SetPrefix("testtool: ")
|
||||||
|
|
||||||
|
if os.Getenv("HAKUREI_BINFMT") == "1" {
|
||||||
|
wantArgs := []string{"/interpreter", "/opt/bin/sample"}
|
||||||
|
if !slices.Equal(os.Args, wantArgs) {
|
||||||
|
log.Fatalf("Args: %q, want %q", os.Args, wantArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile("check", []byte("binfmt"), 0400); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool {
|
environ := slices.DeleteFunc(slices.Clone(os.Environ()), func(s string) bool {
|
||||||
return s == "CURE_JOBS="+strconv.Itoa(runtime.NumCPU())
|
return s == "CURE_JOBS="+strconv.Itoa(runtime.NumCPU())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func TestIRRoundtrip(t *testing.T) {
|
|||||||
)},
|
)},
|
||||||
|
|
||||||
{"exec offline", pkg.NewExec(
|
{"exec offline", pkg.NewExec(
|
||||||
"exec-offline", nil, 0, false,
|
"exec-offline", "", nil, 0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
[]string{"HAKUREI_TEST=1"},
|
[]string{"HAKUREI_TEST=1"},
|
||||||
check.MustAbs("/opt/bin/testtool"),
|
check.MustAbs("/opt/bin/testtool"),
|
||||||
@@ -59,7 +59,7 @@ func TestIRRoundtrip(t *testing.T) {
|
|||||||
)},
|
)},
|
||||||
|
|
||||||
{"exec net", pkg.NewExec(
|
{"exec net", pkg.NewExec(
|
||||||
"exec-net",
|
"exec-net", "",
|
||||||
(*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
|
(*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
|
||||||
0, false,
|
0, false,
|
||||||
pkg.AbsWork,
|
pkg.AbsWork,
|
||||||
|
|||||||
@@ -423,7 +423,7 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
|||||||
msg := message.New(log.New(os.Stderr, "cache: ", 0))
|
msg := message.New(log.New(os.Stderr, "cache: ", 0))
|
||||||
msg.SwapVerbose(testing.Verbose())
|
msg.SwapVerbose(testing.Verbose())
|
||||||
|
|
||||||
flags := tc.flags
|
flags := tc.flags | pkg.CSuppressInit
|
||||||
|
|
||||||
if info.CanDegrade {
|
if info.CanDegrade {
|
||||||
if _, err := landlock.GetABI(); err != nil {
|
if _, err := landlock.GetABI(); err != nil {
|
||||||
@@ -544,7 +544,11 @@ func cureMany(t *testing.T, c *pkg.Cache, steps []cureStep) {
|
|||||||
t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname)
|
t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname)
|
||||||
} else if step.output == nil || checksum != makeChecksumH(step.output.hash()) {
|
} else if step.output == nil || checksum != makeChecksumH(step.output.hash()) {
|
||||||
if pathname != nil {
|
if pathname != nil {
|
||||||
t.Fatal(expectsFrom(pathname.String()))
|
if name, _err := filepath.EvalSymlinks(pathname.String()); _err != nil {
|
||||||
|
t.Fatal(_err)
|
||||||
|
} else {
|
||||||
|
t.Fatal(expectsFrom(name))
|
||||||
|
}
|
||||||
} else if checksum != (unique.Handle[pkg.Checksum]{}) {
|
} else if checksum != (unique.Handle[pkg.Checksum]{}) {
|
||||||
t.Fatalf("Cure: unexpected checksum %s", pkg.Encode(checksum.Value()))
|
t.Fatalf("Cure: unexpected checksum %s", pkg.Encode(checksum.Value()))
|
||||||
}
|
}
|
||||||
@@ -1285,6 +1289,11 @@ func TestErrors(t *testing.T) {
|
|||||||
{"UnsupportedVariantError", pkg.UnsupportedVariantError(
|
{"UnsupportedVariantError", pkg.UnsupportedVariantError(
|
||||||
"rosa",
|
"rosa",
|
||||||
), `unsupported variant "rosa"`},
|
), `unsupported variant "rosa"`},
|
||||||
|
|
||||||
|
{"UnsupportedArchError zero", pkg.UnsupportedArchError(""),
|
||||||
|
"invalid architecture name"},
|
||||||
|
{"UnsupportedArchError", pkg.UnsupportedArchError("riscv64"),
|
||||||
|
"unsupported architecture riscv64"},
|
||||||
}
|
}
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -70,6 +71,7 @@ const (
|
|||||||
Gzip
|
Gzip
|
||||||
Hakurei
|
Hakurei
|
||||||
HakureiDist
|
HakureiDist
|
||||||
|
Hwdata
|
||||||
IPTables
|
IPTables
|
||||||
Kmod
|
Kmod
|
||||||
LIT
|
LIT
|
||||||
@@ -79,8 +81,11 @@ const (
|
|||||||
LibXrandr
|
LibXrandr
|
||||||
LibXrender
|
LibXrender
|
||||||
LibXxf86vm
|
LibXxf86vm
|
||||||
|
Libarchive
|
||||||
Libbsd
|
Libbsd
|
||||||
Libcap
|
Libcap
|
||||||
|
Libconfig
|
||||||
|
LibdisplayInfo
|
||||||
Libdrm
|
Libdrm
|
||||||
Libev
|
Libev
|
||||||
Libexpat
|
Libexpat
|
||||||
@@ -173,6 +178,7 @@ const (
|
|||||||
WaylandProtocols
|
WaylandProtocols
|
||||||
XCB
|
XCB
|
||||||
XCBProto
|
XCBProto
|
||||||
|
XCBUtilKeysyms
|
||||||
XDGDBusProxy
|
XDGDBusProxy
|
||||||
XZ
|
XZ
|
||||||
XorgProto
|
XorgProto
|
||||||
@@ -182,7 +188,8 @@ const (
|
|||||||
// PresetUnexportedStart is the first unexported preset.
|
// PresetUnexportedStart is the first unexported preset.
|
||||||
PresetUnexportedStart
|
PresetUnexportedStart
|
||||||
|
|
||||||
llvmSource = iota - 1
|
stage0Dist = iota - 1
|
||||||
|
llvmSource
|
||||||
// earlyCompilerRT is an early, standalone compiler-rt installation for the
|
// earlyCompilerRT is an early, standalone compiler-rt installation for the
|
||||||
// standalone runtimes build.
|
// standalone runtimes build.
|
||||||
//
|
//
|
||||||
@@ -347,6 +354,9 @@ var (
|
|||||||
// artifactsOnce is for lazy initialisation of artifacts.
|
// artifactsOnce is for lazy initialisation of artifacts.
|
||||||
artifactsOnce [_toolchainEnd][len(artifactsM)]sync.Once
|
artifactsOnce [_toolchainEnd][len(artifactsM)]sync.Once
|
||||||
|
|
||||||
|
// arch is the target architecture.
|
||||||
|
arch = runtime.GOARCH
|
||||||
|
|
||||||
// presetOpts globally modifies behaviour of presets.
|
// presetOpts globally modifies behaviour of presets.
|
||||||
presetOpts int
|
presetOpts int
|
||||||
)
|
)
|
||||||
@@ -358,6 +368,9 @@ const (
|
|||||||
OptLLVMNoLTO
|
OptLLVMNoLTO
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Arch returns the target architecture.
|
||||||
|
func Arch() string { return arch }
|
||||||
|
|
||||||
// Flags returns the current preset flags
|
// Flags returns the current preset flags
|
||||||
func Flags() int { return presetOpts }
|
func Flags() int { return presetOpts }
|
||||||
|
|
||||||
@@ -367,7 +380,12 @@ func zero[T any](p *T) { var v T; *p = v }
|
|||||||
// DropCaches arranges for all cached [pkg.Artifact] to be freed some time after
|
// DropCaches arranges for all cached [pkg.Artifact] to be freed some time after
|
||||||
// it returns. Must not be used concurrently with any other function from this
|
// it returns. Must not be used concurrently with any other function from this
|
||||||
// package.
|
// package.
|
||||||
func DropCaches(flags int) {
|
func DropCaches(targetArch string, flags int) {
|
||||||
|
if targetArch == "" {
|
||||||
|
targetArch = runtime.GOARCH
|
||||||
|
}
|
||||||
|
|
||||||
|
arch = targetArch
|
||||||
presetOpts = flags
|
presetOpts = flags
|
||||||
zero(&artifacts)
|
zero(&artifacts)
|
||||||
zero(&artifactsOnce)
|
zero(&artifactsOnce)
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ func TestLoad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkAll(b *testing.B) {
|
func BenchmarkAll(b *testing.B) {
|
||||||
flags := rosa.Flags()
|
arch, flags := rosa.Arch(), rosa.Flags()
|
||||||
b.Cleanup(func() { rosa.DropCaches(flags) })
|
b.Cleanup(func() { rosa.DropCaches(arch, flags) })
|
||||||
|
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
for i := range rosa.PresetEnd {
|
for i := range rosa.PresetEnd {
|
||||||
@@ -29,7 +29,7 @@ func BenchmarkAll(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
rosa.DropCaches(0)
|
rosa.DropCaches("", 0)
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hakurei.app/fhs"
|
"hakurei.app/fhs"
|
||||||
@@ -88,7 +87,7 @@ func (a busyboxBin) Cure(t *pkg.TContext) (err error) {
|
|||||||
// the https://busybox.net/downloads/binaries/ binary release.
|
// the https://busybox.net/downloads/binaries/ binary release.
|
||||||
func newBusyboxBin() pkg.Artifact {
|
func newBusyboxBin() pkg.Artifact {
|
||||||
var version, url, checksum string
|
var version, url, checksum string
|
||||||
switch runtime.GOARCH {
|
switch arch {
|
||||||
case "amd64":
|
case "amd64":
|
||||||
version = "1.35.0"
|
version = "1.35.0"
|
||||||
url = "https://busybox.net/downloads/binaries/" +
|
url = "https://busybox.net/downloads/binaries/" +
|
||||||
@@ -101,11 +100,11 @@ func newBusyboxBin() pkg.Artifact {
|
|||||||
checksum = "npJjBO7iwhjW6Kx2aXeSxf8kXhVgTCDChOZTTsI8ZfFfa3tbsklxRiidZQdrVERg"
|
checksum = "npJjBO7iwhjW6Kx2aXeSxf8kXhVgTCDChOZTTsI8ZfFfa3tbsklxRiidZQdrVERg"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unsupported target " + runtime.GOARCH)
|
panic("unsupported target " + arch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pkg.NewExec(
|
return pkg.NewExec(
|
||||||
"busybox-bin-"+version, nil, pkg.ExecTimeoutMax, false,
|
"busybox-bin-"+version, arch, nil, pkg.ExecTimeoutMax, false,
|
||||||
fhs.AbsRoot, []string{
|
fhs.AbsRoot, []string{
|
||||||
"PATH=/system/bin",
|
"PATH=/system/bin",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ disable_test t9300-fast-import
|
|||||||
disable_test t0211-trace2-perf
|
disable_test t0211-trace2-perf
|
||||||
disable_test t1517-outside-repo
|
disable_test t1517-outside-repo
|
||||||
disable_test t2200-add-update
|
disable_test t2200-add-update
|
||||||
|
disable_test t0027-auto-crlf
|
||||||
|
disable_test t7513-interpret-trailers
|
||||||
|
disable_test t7703-repack-geometric
|
||||||
`,
|
`,
|
||||||
Check: []string{
|
Check: []string{
|
||||||
"-C t",
|
"-C t",
|
||||||
|
|||||||
@@ -132,6 +132,57 @@ func (t Toolchain) newSPIRVLLVMTranslator() (pkg.Artifact, string) {
|
|||||||
version = "22.1.2"
|
version = "22.1.2"
|
||||||
checksum = "JZAaV5ewYcm-35YA_U2BM2IcsQouZtX1BLZR0zh2vSlfEXMsT5OCtY4Gh5RJkcGy"
|
checksum = "JZAaV5ewYcm-35YA_U2BM2IcsQouZtX1BLZR0zh2vSlfEXMsT5OCtY4Gh5RJkcGy"
|
||||||
)
|
)
|
||||||
|
skipChecks := []string{
|
||||||
|
// error: line 13: OpTypeCooperativeMatrixKHR Scope is limited to Workgroup and Subgroup
|
||||||
|
"cooperative_matrix_constant_null.spvasm",
|
||||||
|
}
|
||||||
|
|
||||||
|
switch arch {
|
||||||
|
case "arm64":
|
||||||
|
skipChecks = append(skipChecks,
|
||||||
|
// LLVM ERROR: unsupported calling convention
|
||||||
|
"DebugInfo/COFF/no-cus.ll",
|
||||||
|
"DebugInfo/Generic/2009-11-05-DeadGlobalVariable.ll",
|
||||||
|
"DebugInfo/Generic/2009-11-10-CurrentFn.ll",
|
||||||
|
"DebugInfo/Generic/2010-01-05-DbgScope.ll",
|
||||||
|
"DebugInfo/Generic/2010-03-12-llc-crash.ll",
|
||||||
|
"DebugInfo/Generic/2010-03-24-MemberFn.ll",
|
||||||
|
"DebugInfo/Generic/2010-04-19-FramePtr.ll",
|
||||||
|
"DebugInfo/Generic/2010-06-29-InlinedFnLocalVar.ll",
|
||||||
|
"DebugInfo/Generic/2010-10-01-crash.ll",
|
||||||
|
"DebugInfo/Generic/PR20038.ll",
|
||||||
|
"DebugInfo/Generic/constant-pointers.ll",
|
||||||
|
"DebugInfo/Generic/dead-argument-order.ll",
|
||||||
|
"DebugInfo/Generic/debug-info-eis-option.ll",
|
||||||
|
"DebugInfo/Generic/def-line.ll",
|
||||||
|
"DebugInfo/Generic/discriminator.ll",
|
||||||
|
"DebugInfo/Generic/dwarf-public-names.ll",
|
||||||
|
"DebugInfo/Generic/enum.ll",
|
||||||
|
"DebugInfo/Generic/func-using-decl.ll",
|
||||||
|
"DebugInfo/Generic/global.ll",
|
||||||
|
"DebugInfo/Generic/imported-name-inlined.ll",
|
||||||
|
"DebugInfo/Generic/incorrect-variable-debugloc1.ll",
|
||||||
|
"DebugInfo/Generic/inline-scopes.ll",
|
||||||
|
"DebugInfo/Generic/inlined-arguments.ll",
|
||||||
|
"DebugInfo/Generic/inlined-vars.ll",
|
||||||
|
"DebugInfo/Generic/linear-dbg-value.ll",
|
||||||
|
"DebugInfo/Generic/linkage-name-abstract.ll",
|
||||||
|
"DebugInfo/Generic/member-order.ll",
|
||||||
|
"DebugInfo/Generic/missing-abstract-variable.ll",
|
||||||
|
"DebugInfo/Generic/multiline.ll",
|
||||||
|
"DebugInfo/Generic/namespace_function_definition.ll",
|
||||||
|
"DebugInfo/Generic/namespace_inline_function_definition.ll",
|
||||||
|
"DebugInfo/Generic/noscopes.ll",
|
||||||
|
"DebugInfo/Generic/ptrsize.ll",
|
||||||
|
"DebugInfo/Generic/restrict.ll",
|
||||||
|
"DebugInfo/Generic/two-cus-from-same-file.ll",
|
||||||
|
"DebugInfo/Generic/version.ll",
|
||||||
|
"DebugInfo/LocalAddressSpace.ll",
|
||||||
|
"DebugInfo/UnknownBaseType.ll",
|
||||||
|
"DebugInfo/expr-opcode.ll",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return t.NewPackage("spirv-llvm-translator", version, newFromGitHub(
|
return t.NewPackage("spirv-llvm-translator", version, newFromGitHub(
|
||||||
"KhronosGroup/SPIRV-LLVM-Translator",
|
"KhronosGroup/SPIRV-LLVM-Translator",
|
||||||
"v"+version, checksum,
|
"v"+version, checksum,
|
||||||
@@ -153,9 +204,7 @@ index c000a77e..86f79b03 100644
|
|||||||
|
|
||||||
// litArgs emits shell syntax
|
// litArgs emits shell syntax
|
||||||
ScriptEarly: `
|
ScriptEarly: `
|
||||||
export LIT_OPTS=` + litArgs(true,
|
export LIT_OPTS=` + litArgs(true, skipChecks...) + `
|
||||||
// error: line 13: OpTypeCooperativeMatrixKHR Scope is limited to Workgroup and Subgroup
|
|
||||||
"cooperative_matrix_constant_null.spvasm") + `
|
|
||||||
`,
|
`,
|
||||||
}, &CMakeHelper{
|
}, &CMakeHelper{
|
||||||
Cache: []KV{
|
Cache: []KV{
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package rosa
|
package rosa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -843,7 +842,7 @@ func (t Toolchain) newGnuTLS() (pkg.Artifact, string) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var configureExtra []KV
|
var configureExtra []KV
|
||||||
switch runtime.GOARCH {
|
switch arch {
|
||||||
case "arm64":
|
case "arm64":
|
||||||
configureExtra = []KV{
|
configureExtra = []KV{
|
||||||
{"disable-hardware-acceleration"},
|
{"disable-hardware-acceleration"},
|
||||||
@@ -1148,7 +1147,7 @@ func (t Toolchain) newGCC() (pkg.Artifact, string) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var configureExtra []KV
|
var configureExtra []KV
|
||||||
switch runtime.GOARCH {
|
switch arch {
|
||||||
case "amd64", "arm64":
|
case "amd64", "arm64":
|
||||||
configureExtra = append(configureExtra, KV{"with-multilib-list", "''"})
|
configureExtra = append(configureExtra, KV{"with-multilib-list", "''"})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package rosa
|
package rosa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"hakurei.app/internal/pkg"
|
"hakurei.app/internal/pkg"
|
||||||
@@ -35,6 +34,10 @@ func (t Toolchain) newGo(
|
|||||||
script string,
|
script string,
|
||||||
extra ...pkg.Artifact,
|
extra ...pkg.Artifact,
|
||||||
) pkg.Artifact {
|
) pkg.Artifact {
|
||||||
|
name := "all"
|
||||||
|
if presetOpts&OptSkipCheck != 0 {
|
||||||
|
name = "make"
|
||||||
|
}
|
||||||
return t.New("go"+version, 0, t.AppendPresets(extra,
|
return t.New("go"+version, 0, t.AppendPresets(extra,
|
||||||
Bash,
|
Bash,
|
||||||
), nil, slices.Concat([]string{
|
), nil, slices.Concat([]string{
|
||||||
@@ -48,7 +51,7 @@ cp -r /usr/src/go /work/system
|
|||||||
cd /work/system/go/src
|
cd /work/system/go/src
|
||||||
chmod -R +w ..
|
chmod -R +w ..
|
||||||
`+script+`
|
`+script+`
|
||||||
./all.bash
|
./`+name+`.bash
|
||||||
|
|
||||||
mkdir /work/system/bin
|
mkdir /work/system/bin
|
||||||
ln -s \
|
ln -s \
|
||||||
@@ -69,7 +72,7 @@ func (t Toolchain) newGoLatest() (pkg.Artifact, string) {
|
|||||||
|
|
||||||
finalEnv []string
|
finalEnv []string
|
||||||
)
|
)
|
||||||
switch runtime.GOARCH {
|
switch arch {
|
||||||
case "amd64":
|
case "amd64":
|
||||||
bootstrapExtra = append(bootstrapExtra, t.newGoBootstrap())
|
bootstrapExtra = append(bootstrapExtra, t.newGoBootstrap())
|
||||||
|
|
||||||
@@ -79,7 +82,7 @@ func (t Toolchain) newGoLatest() (pkg.Artifact, string) {
|
|||||||
finalEnv = append(finalEnv, "CGO_ENABLED=0")
|
finalEnv = append(finalEnv, "CGO_ENABLED=0")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unsupported target " + runtime.GOARCH)
|
panic("unsupported target " + arch)
|
||||||
}
|
}
|
||||||
|
|
||||||
go119 := t.newGo(
|
go119 := t.newGo(
|
||||||
@@ -104,7 +107,7 @@ echo \
|
|||||||
[]string{"CGO_ENABLED=0"}, `
|
[]string{"CGO_ENABLED=0"}, `
|
||||||
sed -i \
|
sed -i \
|
||||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
cmd/link/internal/`+arch+`/obj.go
|
||||||
|
|
||||||
rm \
|
rm \
|
||||||
crypto/tls/handshake_client_test.go \
|
crypto/tls/handshake_client_test.go \
|
||||||
@@ -122,17 +125,17 @@ echo \
|
|||||||
[]string{"CGO_ENABLED=0"}, `
|
[]string{"CGO_ENABLED=0"}, `
|
||||||
sed -i \
|
sed -i \
|
||||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
cmd/link/internal/`+arch+`/obj.go
|
||||||
`, go121,
|
`, go121,
|
||||||
)
|
)
|
||||||
|
|
||||||
go125 := t.newGo(
|
go125 := t.newGo(
|
||||||
"1.25.9",
|
"1.25.10",
|
||||||
"gShJb9uOMk5AxqPSwvn53ZO56S6PyP6nfojzrHUiJ3krAvrgjJpYa6-DPA-jxbpN",
|
"TwKwatkpwal-j9U2sDSRPEdM3YesI4Gm88YgGV59wtU-L85K9gA7UPy9SCxn6PMb",
|
||||||
[]string{"CGO_ENABLED=0"}, `
|
[]string{"CGO_ENABLED=0"}, `
|
||||||
sed -i \
|
sed -i \
|
||||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
cmd/link/internal/`+arch+`/obj.go
|
||||||
|
|
||||||
rm \
|
rm \
|
||||||
os/root_unix_test.go \
|
os/root_unix_test.go \
|
||||||
@@ -141,8 +144,8 @@ rm \
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "1.26.2"
|
version = "1.26.3"
|
||||||
checksum = "v-6BE89_1g3xYf-9oIYpJKFXlo3xKHYJj2_VGkaUq8ZVkIVQmLwrto-xGG03OISH"
|
checksum = "lEiFocZFnN5fKvZzmwVdqc9pYUjAuhzqZGbuiOqxUP4XdcY8yECisKcqsQ_eNn1N"
|
||||||
)
|
)
|
||||||
return t.newGo(
|
return t.newGo(
|
||||||
version,
|
version,
|
||||||
@@ -150,7 +153,7 @@ rm \
|
|||||||
finalEnv, `
|
finalEnv, `
|
||||||
sed -i \
|
sed -i \
|
||||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
cmd/link/internal/`+arch+`/obj.go
|
||||||
sed -i \
|
sed -i \
|
||||||
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
|
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
|
||||||
internal/runtime/gc/scan/scan_amd64.go
|
internal/runtime/gc/scan/scan_amd64.go
|
||||||
|
|||||||
34
internal/rosa/hwdata.go
Normal file
34
internal/rosa/hwdata.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package rosa
|
||||||
|
|
||||||
|
import "hakurei.app/internal/pkg"
|
||||||
|
|
||||||
|
func (t Toolchain) newHwdata() (pkg.Artifact, string) {
|
||||||
|
const (
|
||||||
|
version = "0.407"
|
||||||
|
checksum = "6p1XD0CRuzt6hLfjv4ShKBW934BexmoPkRrmwxD4J63fBVCzVBRHyF8pVJdW_Xjm"
|
||||||
|
)
|
||||||
|
return t.NewPackage("hwdata", version, newFromGitHub(
|
||||||
|
"vcrhonek/hwdata",
|
||||||
|
"v"+version, checksum,
|
||||||
|
), &PackageAttr{
|
||||||
|
Writable: true,
|
||||||
|
EnterSource: true,
|
||||||
|
}, &MakeHelper{
|
||||||
|
// awk: fatal: cannot open file `hwdata.spec' for reading: No such file or directory
|
||||||
|
InPlace: true,
|
||||||
|
|
||||||
|
// lspci: Unknown option 'A' (see "lspci --help")
|
||||||
|
SkipCheck: true,
|
||||||
|
}), version
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
artifactsM[Hwdata] = Metadata{
|
||||||
|
f: Toolchain.newHwdata,
|
||||||
|
|
||||||
|
Name: "hwdata",
|
||||||
|
Description: "contains various hardware identification and configuration data",
|
||||||
|
Website: "https://github.com/vcrhonek/hwdata",
|
||||||
|
|
||||||
|
ID: 5387,
|
||||||
|
}
|
||||||
|
}
|
||||||
100
internal/rosa/libarchive.go
Normal file
100
internal/rosa/libarchive.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package rosa
|
||||||
|
|
||||||
|
import "hakurei.app/internal/pkg"
|
||||||
|
|
||||||
|
func (t Toolchain) newLibarchive() (pkg.Artifact, string) {
|
||||||
|
const (
|
||||||
|
version = "3.8.7"
|
||||||
|
checksum = "CUJK4MDQmZmATClgQBH2Wt-7Ts4iiSUlg1J_TVb6-5IK3rVUgVLIMc5k-bnWB9w3"
|
||||||
|
)
|
||||||
|
return t.NewPackage("libarchive", version, newFromGitHub(
|
||||||
|
"libarchive/libarchive",
|
||||||
|
"v"+version, checksum,
|
||||||
|
), &PackageAttr{
|
||||||
|
Paths: []pkg.ExecPath{
|
||||||
|
pkg.Path(AbsUsrSrc.Append(
|
||||||
|
"CTestCustom.cmake",
|
||||||
|
), false, pkg.NewFile("CTestCustom.cmake", []byte(`
|
||||||
|
list(APPEND CTEST_CUSTOM_TESTS_IGNORE
|
||||||
|
"libarchive_test_archive_string_conversion_fail_c"
|
||||||
|
"libarchive_test_archive_string_conversion_fail_latin1"
|
||||||
|
"libarchive_test_archive_string_update_utf8_koi8"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_KOI8R_UTF8"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_KOI8R_CP866"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_CP1251_UTF8"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_Russian_Russia"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_EUCJP_UTF8"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_EUCJP_CP932"
|
||||||
|
"libarchive_test_gnutar_filename_encoding_CP932_UTF8"
|
||||||
|
"libarchive_test_pax_filename_encoding_KOI8R"
|
||||||
|
"libarchive_test_pax_filename_encoding_CP1251"
|
||||||
|
"libarchive_test_pax_filename_encoding_EUCJP"
|
||||||
|
"libarchive_test_pax_filename_encoding_CP932"
|
||||||
|
"libarchive_test_read_format_cpio_filename_UTF8_eucJP"
|
||||||
|
"libarchive_test_read_format_cpio_filename_CP866_KOI8R"
|
||||||
|
"libarchive_test_read_format_cpio_filename_KOI8R_CP866"
|
||||||
|
"libarchive_test_read_format_cpio_filename_UTF8_KOI8R"
|
||||||
|
"libarchive_test_read_format_cpio_filename_UTF8_CP866"
|
||||||
|
"libarchive_test_read_format_cpio_filename_eucJP_CP932"
|
||||||
|
"libarchive_test_read_format_cpio_filename_UTF8_CP932"
|
||||||
|
"libarchive_test_read_format_cpio_filename_CP866_CP1251"
|
||||||
|
"libarchive_test_read_format_cpio_filename_CP866_CP1251_win"
|
||||||
|
"libarchive_test_read_format_cpio_filename_KOI8R_CP1251"
|
||||||
|
"libarchive_test_read_format_cpio_filename_UTF8_CP1251"
|
||||||
|
"libarchive_test_read_format_gtar_filename_CP866_KOI8R"
|
||||||
|
"libarchive_test_read_format_gtar_filename_KOI8R_CP866"
|
||||||
|
"libarchive_test_read_format_gtar_filename_eucJP_CP932"
|
||||||
|
"libarchive_test_read_format_gtar_filename_CP866_CP1251"
|
||||||
|
"libarchive_test_read_format_gtar_filename_CP866_CP1251_win"
|
||||||
|
"libarchive_test_read_format_gtar_filename_KOI8R_CP1251"
|
||||||
|
"libarchive_test_read_format_rar_unicode_CP932"
|
||||||
|
"libarchive_test_read_format_zip_filename_CP932_eucJP"
|
||||||
|
"libarchive_test_read_format_zip_filename_UTF8_eucJP"
|
||||||
|
"libarchive_test_read_format_zip_filename_CP866_KOI8R"
|
||||||
|
"libarchive_test_read_format_zip_filename_KOI8R_CP866"
|
||||||
|
"libarchive_test_read_format_zip_filename_UTF8_KOI8R"
|
||||||
|
"libarchive_test_read_format_zip_filename_UTF8_CP866"
|
||||||
|
"libarchive_test_read_format_zip_filename_CP932_CP932"
|
||||||
|
"libarchive_test_read_format_zip_filename_UTF8_CP932"
|
||||||
|
"libarchive_test_read_format_zip_filename_CP866_CP1251"
|
||||||
|
"libarchive_test_read_format_zip_filename_CP866_CP1251_win"
|
||||||
|
"libarchive_test_read_format_zip_filename_KOI8R_CP1251"
|
||||||
|
"libarchive_test_read_format_zip_filename_UTF8_CP1251"
|
||||||
|
"libarchive_test_ustar_filename_encoding_KOI8R_UTF8"
|
||||||
|
"libarchive_test_ustar_filename_encoding_KOI8R_CP866"
|
||||||
|
"libarchive_test_ustar_filename_encoding_CP1251_UTF8"
|
||||||
|
"libarchive_test_ustar_filename_encoding_Russian_Russia"
|
||||||
|
"libarchive_test_ustar_filename_encoding_EUCJP_UTF8"
|
||||||
|
"libarchive_test_ustar_filename_encoding_EUCJP_CP932"
|
||||||
|
"libarchive_test_ustar_filename_encoding_CP932_UTF8"
|
||||||
|
"libarchive_test_zip_filename_encoding_KOI8R"
|
||||||
|
"libarchive_test_zip_filename_encoding_ru_RU_CP1251"
|
||||||
|
"libarchive_test_zip_filename_encoding_Russian_Russia"
|
||||||
|
"libarchive_test_zip_filename_encoding_EUCJP"
|
||||||
|
"libarchive_test_zip_filename_encoding_CP932"
|
||||||
|
"libarchive_test_read_format_cab_filename"
|
||||||
|
"libarchive_test_read_format_lha_filename"
|
||||||
|
"libarchive_test_read_format_tar_filename"
|
||||||
|
"libarchive_test_read_format_ustar_filename"
|
||||||
|
"libarchive_test_read_append_wrong_filter"
|
||||||
|
)
|
||||||
|
`))),
|
||||||
|
},
|
||||||
|
|
||||||
|
Writable: true,
|
||||||
|
ScriptEarly: `
|
||||||
|
install -Dv /usr/src/CTestCustom.cmake /cure/
|
||||||
|
`,
|
||||||
|
}, (*CMakeHelper)(nil)), version
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
artifactsM[Libarchive] = Metadata{
|
||||||
|
f: Toolchain.newLibarchive,
|
||||||
|
|
||||||
|
Name: "libarchive",
|
||||||
|
Description: "multi-format archive and compression library",
|
||||||
|
Website: "https://www.libarchive.org/",
|
||||||
|
|
||||||
|
ID: 1558,
|
||||||
|
}
|
||||||
|
}
|
||||||
50
internal/rosa/libconfig.go
Normal file
50
internal/rosa/libconfig.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package rosa
|
||||||
|
|
||||||
|
import "hakurei.app/internal/pkg"
|
||||||
|
|
||||||
|
func (t Toolchain) newLibconfig() (pkg.Artifact, string) {
|
||||||
|
const (
|
||||||
|
version = "1.8.2"
|
||||||
|
checksum = "fD32hjeAZuTz98g6WYHRwsxphrgrEFqxi5Z1jlJemPckPBfxpS3i5HgshAuA6vmT"
|
||||||
|
)
|
||||||
|
return t.NewPackage("libconfig", version, newFromGitHub(
|
||||||
|
"hyperrealm/libconfig",
|
||||||
|
"v"+version,
|
||||||
|
checksum,
|
||||||
|
), &PackageAttr{
|
||||||
|
Patches: []KV{
|
||||||
|
{"disable-broken-tests", `diff --git a/tests/tests.c b/tests/tests.c
|
||||||
|
index eba7eae..f916d2e 100644
|
||||||
|
--- a/tests/tests.c
|
||||||
|
+++ b/tests/tests.c
|
||||||
|
@@ -753,7 +753,6 @@ int main(int argc, char **argv)
|
||||||
|
int failures;
|
||||||
|
|
||||||
|
TT_SUITE_START(LibConfigTests);
|
||||||
|
- TT_SUITE_TEST(LibConfigTests, ParsingAndFormatting);
|
||||||
|
TT_SUITE_TEST(LibConfigTests, ParseInvalidFiles);
|
||||||
|
TT_SUITE_TEST(LibConfigTests, ParseInvalidStrings);
|
||||||
|
TT_SUITE_TEST(LibConfigTests, BigInt1);
|
||||||
|
@@ -768,7 +767,6 @@ int main(int argc, char **argv)
|
||||||
|
TT_SUITE_TEST(LibConfigTests, OverrideSetting);
|
||||||
|
TT_SUITE_TEST(LibConfigTests, SettingLookups);
|
||||||
|
TT_SUITE_TEST(LibConfigTests, ReadStream);
|
||||||
|
- TT_SUITE_TEST(LibConfigTests, BinaryAndHex);
|
||||||
|
TT_SUITE_RUN(LibConfigTests);
|
||||||
|
failures = TT_SUITE_NUM_FAILURES(LibConfigTests);
|
||||||
|
TT_SUITE_END(LibConfigTests);
|
||||||
|
`},
|
||||||
|
},
|
||||||
|
}, (*CMakeHelper)(nil)), version
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
artifactsM[Libconfig] = Metadata{
|
||||||
|
f: Toolchain.newLibconfig,
|
||||||
|
|
||||||
|
Name: "libconfig",
|
||||||
|
Description: "a simple library for processing structured configuration files",
|
||||||
|
Website: "https://hyperrealm.github.io/libconfig/",
|
||||||
|
|
||||||
|
ID: 1580,
|
||||||
|
}
|
||||||
|
}
|
||||||
30
internal/rosa/libdisplay-info.go
Normal file
30
internal/rosa/libdisplay-info.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package rosa
|
||||||
|
|
||||||
|
import "hakurei.app/internal/pkg"
|
||||||
|
|
||||||
|
func (t Toolchain) newLibdisplayInfo() (pkg.Artifact, string) {
|
||||||
|
const (
|
||||||
|
version = "0.3.0"
|
||||||
|
checksum = "yjOqPUHHYgRtpqGw5RI1n2Q1_hO5j0LiFNMbjcRWV4Nf71XwwoC9fZMlKBDeLchT"
|
||||||
|
)
|
||||||
|
return t.NewPackage("libdisplay-info", version, newFromGitLab(
|
||||||
|
"gitlab.freedesktop.org",
|
||||||
|
"emersion/libdisplay-info",
|
||||||
|
version, checksum,
|
||||||
|
), nil, (*MesonHelper)(nil),
|
||||||
|
Diffutils,
|
||||||
|
|
||||||
|
Hwdata,
|
||||||
|
), version
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
artifactsM[LibdisplayInfo] = Metadata{
|
||||||
|
f: Toolchain.newLibdisplayInfo,
|
||||||
|
|
||||||
|
Name: "libdisplay-info",
|
||||||
|
Description: "EDID and DisplayID library",
|
||||||
|
Website: "https://gitlab.freedesktop.org/emersion/libdisplay-info",
|
||||||
|
|
||||||
|
ID: 326668,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,6 @@ package rosa
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -248,7 +247,7 @@ func (t Toolchain) newLLVM() (pkg.Artifact, string) {
|
|||||||
// unwind: fails on musl
|
// unwind: fails on musl
|
||||||
"eh_frame_fde_pc_range",
|
"eh_frame_fde_pc_range",
|
||||||
}
|
}
|
||||||
switch runtime.GOARCH {
|
switch arch {
|
||||||
case "arm64":
|
case "arm64":
|
||||||
skipChecks = append(skipChecks,
|
skipChecks = append(skipChecks,
|
||||||
// LLVM: intermittently crashes
|
// LLVM: intermittently crashes
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ func (t Toolchain) newMksh() (pkg.Artifact, string) {
|
|||||||
version = "59c"
|
version = "59c"
|
||||||
checksum = "0Zj-k4nXEu3IuJY4lvwD2OrC2t27GdZj8SPy4DoaeuBRH1padWb7oREpYgwY8JNq"
|
checksum = "0Zj-k4nXEu3IuJY4lvwD2OrC2t27GdZj8SPy4DoaeuBRH1padWb7oREpYgwY8JNq"
|
||||||
)
|
)
|
||||||
|
scriptTest := "./test.sh -C regress:no-ctty\n"
|
||||||
|
if presetOpts&OptSkipCheck != 0 {
|
||||||
|
scriptTest = ""
|
||||||
|
}
|
||||||
return t.New("mksh-"+version, 0, t.AppendPresets(nil,
|
return t.New("mksh-"+version, 0, t.AppendPresets(nil,
|
||||||
Perl,
|
Perl,
|
||||||
Coreutils,
|
Coreutils,
|
||||||
@@ -18,8 +22,7 @@ cd "$(mktemp -d)"
|
|||||||
sh /usr/src/mksh/Build.sh -r
|
sh /usr/src/mksh/Build.sh -r
|
||||||
CPPFLAGS="${CPPFLAGS} -DMKSH_BINSHPOSIX -DMKSH_BINSHREDUCED" \
|
CPPFLAGS="${CPPFLAGS} -DMKSH_BINSHPOSIX -DMKSH_BINSHREDUCED" \
|
||||||
sh /usr/src/mksh/Build.sh -r -L
|
sh /usr/src/mksh/Build.sh -r -L
|
||||||
./test.sh -C regress:no-ctty
|
`+scriptTest+`
|
||||||
|
|
||||||
mkdir -p /work/system/bin/
|
mkdir -p /work/system/bin/
|
||||||
cp -v mksh /work/system/bin/
|
cp -v mksh /work/system/bin/
|
||||||
cp -v lksh /work/system/bin/sh
|
cp -v lksh /work/system/bin/sh
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ EOF
|
|||||||
{"disable-download"},
|
{"disable-download"},
|
||||||
{"disable-docs"},
|
{"disable-docs"},
|
||||||
|
|
||||||
|
{"static"},
|
||||||
{"target-list-exclude", "" +
|
{"target-list-exclude", "" +
|
||||||
// fails to load firmware
|
// fails to load firmware
|
||||||
"ppc-linux-user," +
|
"ppc-linux-user," +
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package rosa
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -54,10 +53,9 @@ var (
|
|||||||
AbsSystem = fhs.AbsRoot.Append("system")
|
AbsSystem = fhs.AbsRoot.Append("system")
|
||||||
)
|
)
|
||||||
|
|
||||||
// linuxArch returns the architecture name used by linux corresponding to
|
// linuxArch returns the architecture name used by linux corresponding to arch.
|
||||||
// [runtime.GOARCH].
|
|
||||||
func linuxArch() string {
|
func linuxArch() string {
|
||||||
switch runtime.GOARCH {
|
switch arch {
|
||||||
case "amd64":
|
case "amd64":
|
||||||
return "x86_64"
|
return "x86_64"
|
||||||
case "arm64":
|
case "arm64":
|
||||||
@@ -66,11 +64,11 @@ func linuxArch() string {
|
|||||||
return "riscv64"
|
return "riscv64"
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic("unsupported target " + runtime.GOARCH)
|
panic("unsupported target " + arch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// triplet returns the Rosa OS host triple corresponding to [runtime.GOARCH].
|
// triplet returns the Rosa OS host triple corresponding to arch.
|
||||||
func triplet() string {
|
func triplet() string {
|
||||||
return linuxArch() + "-rosa-linux-musl"
|
return linuxArch() + "-rosa-linux-musl"
|
||||||
}
|
}
|
||||||
@@ -80,9 +78,9 @@ type perArch[T any] map[string]T
|
|||||||
|
|
||||||
// unwrap returns the value for the current architecture.
|
// unwrap returns the value for the current architecture.
|
||||||
func (p perArch[T]) unwrap() T {
|
func (p perArch[T]) unwrap() T {
|
||||||
v, ok := p[runtime.GOARCH]
|
v, ok := p[arch]
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("unsupported target " + runtime.GOARCH)
|
panic("unsupported target " + arch)
|
||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -268,7 +266,7 @@ func (t Toolchain) New(
|
|||||||
support = append(support, extra...)
|
support = append(support, extra...)
|
||||||
support = append(support, cureEtc{})
|
support = append(support, cureEtc{})
|
||||||
if t == toolchainStage0 {
|
if t == toolchainStage0 {
|
||||||
support = append(support, NewStage0())
|
support = append(support, t.Load(stage0Dist))
|
||||||
} else {
|
} else {
|
||||||
support = append(support, _toolchainBusybox.New("gentoo", 0, nil, nil, nil, `
|
support = append(support, _toolchainBusybox.New("gentoo", 0, nil, nil, nil, `
|
||||||
tar -C /work -xf /usr/src/stage3.tar.xz
|
tar -C /work -xf /usr/src/stage3.tar.xz
|
||||||
@@ -327,7 +325,7 @@ mkdir -vp /work/system/bin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return pkg.NewExec(
|
return pkg.NewExec(
|
||||||
name, knownChecksum, pkg.ExecTimeoutMax, flag&TExclusive != 0,
|
name, arch, knownChecksum, pkg.ExecTimeoutMax, flag&TExclusive != 0,
|
||||||
fhs.AbsRoot, env,
|
fhs.AbsRoot, env,
|
||||||
AbsSystem.Append("bin", "sh"),
|
AbsSystem.Append("bin", "sh"),
|
||||||
[]string{"sh", absCureScript.String()},
|
[]string{"sh", absCureScript.String()},
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
rosa.DropCaches(rosa.OptLLVMNoLTO)
|
rosa.DropCaches("", rosa.OptLLVMNoLTO)
|
||||||
container.TryArgv0(nil)
|
container.TryArgv0(nil)
|
||||||
|
|
||||||
code := m.Run()
|
code := m.Run()
|
||||||
@@ -94,14 +94,14 @@ func TestCureAll(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkStage3(b *testing.B) {
|
func BenchmarkStage3(b *testing.B) {
|
||||||
flags := rosa.Flags()
|
arch, flags := rosa.Arch(), rosa.Flags()
|
||||||
b.Cleanup(func() { rosa.DropCaches(flags) })
|
b.Cleanup(func() { rosa.DropCaches(arch, flags) })
|
||||||
|
|
||||||
for b.Loop() {
|
for b.Loop() {
|
||||||
rosa.Std.Load(rosa.LLVM)
|
rosa.Std.Load(rosa.LLVM)
|
||||||
|
|
||||||
b.StopTimer()
|
b.StopTimer()
|
||||||
rosa.DropCaches(0)
|
rosa.DropCaches("", 0)
|
||||||
b.StartTimer()
|
b.StartTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package rosa
|
package rosa
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
|
||||||
|
|
||||||
"hakurei.app/fhs"
|
"hakurei.app/fhs"
|
||||||
"hakurei.app/internal/pkg"
|
"hakurei.app/internal/pkg"
|
||||||
)
|
)
|
||||||
@@ -32,25 +30,32 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func init() {
|
||||||
// stage0 stores the tarball unpack artifact.
|
const version = "20260504"
|
||||||
stage0 pkg.Artifact
|
artifactsM[stage0Dist] = Metadata{
|
||||||
// stage0Once is for lazy initialisation of stage0.
|
f: func(Toolchain) (pkg.Artifact, string) {
|
||||||
stage0Once sync.Once
|
return newTar(
|
||||||
)
|
"https://hakurei.app/seed/"+version+"/"+
|
||||||
|
"stage0-"+triplet()+".tar.bz2",
|
||||||
|
perArch[string]{
|
||||||
|
"amd64": "IQjFDkiAVLo1XzflgMMiLP3gnVY2hhDMTzl-QqJDCQhcLQ3lLtRzjI5WCxGyW_lk",
|
||||||
|
"arm64": "6fmwl2Umx2QssKQvxxb1JOGkAjzfA_MXKku0jVdGjYGb35OvwEVA5NYtd0HIy3yH",
|
||||||
|
"riscv64": "Z2ODV0rIoo9iQRUIu35bsaOBeXc_9qQfGcyb2aGneatzNUJlXh5emSpEV2bOklUL",
|
||||||
|
}.unwrap(),
|
||||||
|
pkg.TarBzip2,
|
||||||
|
), version
|
||||||
|
},
|
||||||
|
|
||||||
// NewStage0 returns a stage0 distribution created from curing [Stage0].
|
Name: "stage0-dist",
|
||||||
func NewStage0() pkg.Artifact {
|
Description: "Rosa OS stage0 bootstrap seed",
|
||||||
stage0Once.Do(func() {
|
}
|
||||||
stage0 = newTar(
|
}
|
||||||
"https://hakurei.app/seed/20260504/"+
|
|
||||||
"stage0-"+triplet()+".tar.bz2",
|
// HasStage0 returns whether a stage0 distribution is available.
|
||||||
perArch[string]{
|
func HasStage0() (ok bool) {
|
||||||
"amd64": "IQjFDkiAVLo1XzflgMMiLP3gnVY2hhDMTzl-QqJDCQhcLQ3lLtRzjI5WCxGyW_lk",
|
func() {
|
||||||
"arm64": "6fmwl2Umx2QssKQvxxb1JOGkAjzfA_MXKku0jVdGjYGb35OvwEVA5NYtd0HIy3yH",
|
defer func() { ok = recover() == nil }()
|
||||||
}.unwrap(),
|
toolchainStage0.Load(stage0Dist)
|
||||||
pkg.TarBzip2,
|
}()
|
||||||
)
|
return
|
||||||
})
|
|
||||||
return stage0
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
package rosa
|
package rosa
|
||||||
|
|
||||||
import (
|
import "hakurei.app/internal/pkg"
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"hakurei.app/internal/pkg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (t Toolchain) newTamaGo() (pkg.Artifact, string) {
|
func (t Toolchain) newTamaGo() (pkg.Artifact, string) {
|
||||||
const (
|
const (
|
||||||
@@ -26,7 +22,7 @@ chmod -R +w ..
|
|||||||
|
|
||||||
sed -i \
|
sed -i \
|
||||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
cmd/link/internal/`+arch+`/obj.go
|
||||||
sed -i \
|
sed -i \
|
||||||
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
|
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
|
||||||
internal/runtime/gc/scan/scan_amd64.go
|
internal/runtime/gc/scan/scan_amd64.go
|
||||||
|
|||||||
@@ -186,6 +186,37 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Toolchain) newXCBUtilKeysyms() (pkg.Artifact, string) {
|
||||||
|
const (
|
||||||
|
version = "0.4.1"
|
||||||
|
checksum = "-EEje12UEjtFBuIjb6Fy4cxEghV20BXwQ1BLvhtvSuVcrFkp_X-ZHRM48wAspXZ4"
|
||||||
|
)
|
||||||
|
return t.NewPackage("xcb-util-keysyms", version, newTar(
|
||||||
|
"https://xcb.freedesktop.org/dist/xcb-util-keysyms-"+version+".tar.gz",
|
||||||
|
checksum,
|
||||||
|
pkg.TarGzip,
|
||||||
|
), nil, (*MakeHelper)(nil),
|
||||||
|
PkgConfig,
|
||||||
|
|
||||||
|
XCB,
|
||||||
|
), version
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
artifactsM[XCBUtilKeysyms] = Metadata{
|
||||||
|
f: Toolchain.newXCBUtilKeysyms,
|
||||||
|
|
||||||
|
Name: "xcb-util-keysyms",
|
||||||
|
Description: "standard X key constants and conversion to/from keycodes",
|
||||||
|
Website: "https://gitlab.freedesktop.org/xorg/lib/libxcb-keysyms",
|
||||||
|
|
||||||
|
Dependencies: P{
|
||||||
|
XCB,
|
||||||
|
},
|
||||||
|
|
||||||
|
ID: 5168,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t Toolchain) newLibX11() (pkg.Artifact, string) {
|
func (t Toolchain) newLibX11() (pkg.Artifact, string) {
|
||||||
const (
|
const (
|
||||||
version = "1.8.13"
|
version = "1.8.13"
|
||||||
|
|||||||
3
make.sh
Executable file
3
make.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
|
HAKUREI_DIST_MAKE=1 exec "$(dirname -- "$0")/cmd/dist/dist.sh"
|
||||||
@@ -35,7 +35,7 @@ package
|
|||||||
|
|
||||||
|
|
||||||
*Default:*
|
*Default:*
|
||||||
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.4.1> `
|
` <derivation hakurei-static-x86_64-unknown-linux-musl-0.4.2> `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -842,7 +842,7 @@ package
|
|||||||
|
|
||||||
|
|
||||||
*Default:*
|
*Default:*
|
||||||
` <derivation hakurei-hsu-0.4.1> `
|
` <derivation hakurei-hsu-0.4.2> `
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
buildGo126Module rec {
|
buildGo126Module rec {
|
||||||
pname = "hakurei";
|
pname = "hakurei";
|
||||||
version = "0.4.1";
|
version = "0.4.2";
|
||||||
|
|
||||||
srcFiltered = builtins.path {
|
srcFiltered = builtins.path {
|
||||||
name = "${pname}-src";
|
name = "${pname}-src";
|
||||||
|
|||||||
Reference in New Issue
Block a user