forked from rosa/hakurei
Compare commits
138 Commits
bb8a80b540
...
wip-gwt
| Author | SHA1 | Date | |
|---|---|---|---|
| b4348a3db3 | |||
| 486180c11f | |||
| d3d68ad472 | |||
| d2dc6a716d | |||
|
c32c06b2e8
|
|||
|
61199f734c
|
|||
|
87cf0d4e6b
|
|||
|
cf0dffa0f5
|
|||
|
686d7ec63a
|
|||
|
4c653b1151
|
|||
|
0b0a63d151
|
|||
|
6231cfe2aa
|
|||
|
712e80890b
|
|||
|
3fe7d48014
|
|||
|
16f9d39427
|
|||
|
c1cd5ba07b
|
|||
|
7b0cd2e472
|
|||
|
e580307528
|
|||
|
ee1dffb676
|
|||
|
f095fcf181
|
|||
|
ca8a130130
|
|||
|
0ad6b00e41
|
|||
|
ad0f1cf36b
|
|||
|
b12d924fa2
|
|||
|
c31d8ae41a
|
|||
|
6dbbf15c0e
|
|||
|
be7de68a42
|
|||
|
a759cf3666
|
|||
|
8c2dd3e984
|
|||
|
67038d5af4
|
|||
|
53d8d12e7f
|
|||
|
7997d79e56
|
|||
|
f2f1726190
|
|||
|
f63203cb0a
|
|||
|
19555c7670
|
|||
|
a3beab8959
|
|||
|
2ea786d6a9
|
|||
|
747d4ec4b0
|
|||
|
b76e6f6519
|
|||
|
840d8f68bf
|
|||
|
4bede7ecdd
|
|||
|
487a03b5a3
|
|||
|
8f3c22896a
|
|||
|
a167c1aba5
|
|||
|
a6008ef68b
|
|||
|
5228b27362
|
|||
|
f00d3a07ad
|
|||
|
f9538bc21b
|
|||
|
6ae5efec56
|
|||
|
14f4c59c8c
|
|||
|
688d43417b
|
|||
|
9f8fafa39b
|
|||
|
6643cfbeee
|
|||
|
dcde38f2e9
|
|||
|
deebbf6b1a
|
|||
|
0c557798bc
|
|||
|
327e6ed5a2
|
|||
|
76c7a423a9
|
|||
|
6e113b8836
|
|||
|
ce9f4b5f71
|
|||
|
8f727273ef
|
|||
|
d0a63b942e
|
|||
|
7f2126df32
|
|||
|
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
|
|||
|
9aad98d409
|
|||
|
b0d06b67dc
|
|||
|
089100f29d
|
|||
|
dfd26abf6c
|
|||
|
617ee21647
|
|||
|
15cdb37ec2
|
|||
|
1f0bdc7aca
|
|||
|
e3ffe85670
|
|||
|
f4403ba5cd
|
|||
|
5a26895a22
|
|||
|
09d9f766a9
|
|||
|
6558169666
|
|||
|
cccf970c57
|
|||
|
57ffb21690
|
|||
|
9c560b455a
|
|||
|
4c7c0fbfc6
|
|||
|
18b3b7904e
|
|||
|
fefefdf734
|
|||
|
b84bb09a80
|
|||
|
337bf20f50
|
|||
|
1cb792cf6e
|
|||
|
b2b40b07e8
|
|||
|
da11b26ec1
|
|||
|
024489e800
|
|||
|
0f795712b0
|
|||
|
7e2210ff71
|
|||
|
a71a008f3c
|
|||
|
162265b47e
|
|||
|
3fa7ac04e4
|
|||
|
bf2867d653
|
|||
|
ec0f0f6507
|
|||
|
a77a802955
|
|||
|
4407e14dfc
|
|||
|
e024d3184a
|
|||
|
8e1bf00c2d
|
|||
|
b111e22050
|
|||
|
1fa458c0be
|
|||
|
2c7ae67a67
|
|||
|
3826621b21
|
|||
|
041b505c2e
|
|||
|
e6debce649
|
|||
|
aa26b86fce
|
|||
|
a57a8fd5d8
|
|||
|
1d5d063d6a
|
|||
|
e61628a34e
|
|||
|
5a18f14929
|
|||
|
f12880688d
|
+2
-1
@@ -7,7 +7,8 @@
|
||||
|
||||
# go generate
|
||||
/cmd/hakurei/LICENSE
|
||||
/internal/pkg/testdata/testtool
|
||||
/cmd/mbf/internal/pkgserver/ui/static
|
||||
/internal/pkg/internal/testtool/testtool
|
||||
/internal/rosa/hakurei_current.tar.gz
|
||||
|
||||
# cmd/dist default destination
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
TOOLCHAIN_VERSION="$(go version)"
|
||||
cd "$(dirname -- "$0")/"
|
||||
echo "# Building cmd/dist using ${TOOLCHAIN_VERSION}."
|
||||
go run -v --tags=dist ./cmd/dist
|
||||
HAKUREI_DIST_MAKE='' exec "$(dirname -- "$0")/cmd/dist/dist.sh"
|
||||
|
||||
@@ -4,15 +4,23 @@ import "strings"
|
||||
|
||||
const (
|
||||
// SpecialOverlayEscape is the escape string for overlay mount options.
|
||||
//
|
||||
// Deprecated: This is no longer used and will be removed in 0.5.
|
||||
SpecialOverlayEscape = `\`
|
||||
// SpecialOverlayOption is the separator string between overlay mount options.
|
||||
//
|
||||
// Deprecated: This is no longer used and will be removed in 0.5.
|
||||
SpecialOverlayOption = ","
|
||||
// SpecialOverlayPath is the separator string between overlay paths.
|
||||
//
|
||||
// Deprecated: This is no longer used and will be removed in 0.5.
|
||||
SpecialOverlayPath = ":"
|
||||
)
|
||||
|
||||
// EscapeOverlayDataSegment escapes a string for formatting into the data
|
||||
// argument of an overlay mount system call.
|
||||
//
|
||||
// Deprecated: This is no longer used and will be removed in 0.5.
|
||||
func EscapeOverlayDataSegment(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
|
||||
+10
@@ -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
|
||||
Vendored
+26
-14
@@ -42,14 +42,19 @@ func mustRun(ctx context.Context, name string, arg ...string) {
|
||||
var comp []byte
|
||||
|
||||
func main() {
|
||||
fmt.Println()
|
||||
log.SetFlags(0)
|
||||
log.SetPrefix("# ")
|
||||
log.SetPrefix("")
|
||||
|
||||
verbose := os.Getenv("VERBOSE") != ""
|
||||
runTests := os.Getenv("HAKUREI_DIST_MAKE") == ""
|
||||
version := getenv("HAKUREI_VERSION", "untagged")
|
||||
prefix := getenv("PREFIX", "/usr")
|
||||
destdir := getenv("DESTDIR", "dist")
|
||||
|
||||
if verbose {
|
||||
log.Println()
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(destdir, 0755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -76,12 +81,17 @@ func main() {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
|
||||
log.Println("Building hakurei.")
|
||||
verboseFlag := "-v"
|
||||
if !verbose {
|
||||
verboseFlag = "-buildvcs=false"
|
||||
}
|
||||
|
||||
log.Printf("Building hakurei for %s/%s.", runtime.GOOS, runtime.GOARCH)
|
||||
mustRun(ctx, "go", "generate", "./...")
|
||||
mustRun(
|
||||
ctx, "go", "build",
|
||||
"-trimpath",
|
||||
"-v", "-o", s,
|
||||
verboseFlag, "-o", s,
|
||||
"-ldflags=-s -w "+
|
||||
"-buildid= -linkmode external -extldflags=-static "+
|
||||
"-X hakurei.app/internal/info.buildVersion="+version+" "+
|
||||
@@ -90,17 +100,19 @@ func main() {
|
||||
"-X main.hakureiPath="+prefix+"/bin/hakurei",
|
||||
"./...",
|
||||
)
|
||||
fmt.Println()
|
||||
log.Println()
|
||||
|
||||
log.Println("Testing Hakurei.")
|
||||
mustRun(
|
||||
ctx, "go", "test",
|
||||
"-ldflags=-buildid= -linkmode external -extldflags=-static",
|
||||
"./...",
|
||||
)
|
||||
fmt.Println()
|
||||
if runTests {
|
||||
log.Println("##### Testing Hakurei.")
|
||||
mustRun(
|
||||
ctx, "go", "test",
|
||||
"-ldflags=-buildid= -linkmode external -extldflags=-static",
|
||||
"./...",
|
||||
)
|
||||
log.Println()
|
||||
}
|
||||
|
||||
log.Println("Creating distribution.")
|
||||
log.Println("##### Creating distribution.")
|
||||
const suffix = ".tar.gz"
|
||||
distName := "hakurei-" + version + "-" + runtime.GOARCH
|
||||
var f *os.File
|
||||
@@ -121,7 +133,7 @@ func main() {
|
||||
}()
|
||||
|
||||
h := sha512.New()
|
||||
gw := gzip.NewWriter(io.MultiWriter(f, h))
|
||||
gw, _ := gzip.NewWriterLevel(io.MultiWriter(f, h), gzip.BestCompression)
|
||||
tw := tar.NewWriter(gw)
|
||||
|
||||
mustWriteHeader := func(name string, size int64, mode os.FileMode) {
|
||||
|
||||
+46
-2
@@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/internal/pkg"
|
||||
"hakurei.app/message"
|
||||
)
|
||||
@@ -19,8 +20,15 @@ type cache struct {
|
||||
// Should generally not be used directly.
|
||||
c *pkg.Cache
|
||||
|
||||
cures, jobs int
|
||||
hostAbstract, idle bool
|
||||
cures, jobs int
|
||||
// Primarily to work around missing landlock LSM.
|
||||
hostAbstract bool
|
||||
// Set SCHED_IDLE.
|
||||
idle bool
|
||||
// Unset [pkg.CSuppressInit].
|
||||
verboseInit bool
|
||||
// Loaded artifact of [rosa.QEMU].
|
||||
qemu pkg.Artifact
|
||||
|
||||
base string
|
||||
}
|
||||
@@ -45,6 +53,9 @@ func (cache *cache) open() (err error) {
|
||||
if cache.hostAbstract {
|
||||
flags |= pkg.CHostAbstract
|
||||
}
|
||||
if !cache.verboseInit {
|
||||
flags |= pkg.CSuppressInit
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
@@ -70,6 +81,39 @@ func (cache *cache) open() (err error) {
|
||||
cache.jobs,
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
+1
-14
@@ -17,25 +17,12 @@ func commandInfo(
|
||||
args []string,
|
||||
w io.Writer,
|
||||
writeStatus bool,
|
||||
reportPath string,
|
||||
r *rosa.Report,
|
||||
) (err error) {
|
||||
if len(args) == 0 {
|
||||
return errors.New("info requires at least 1 argument")
|
||||
}
|
||||
|
||||
var r *rosa.Report
|
||||
if reportPath != "" {
|
||||
if r, err = rosa.OpenReport(reportPath); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := r.Close(); err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
defer r.HandleAccess(&err)()
|
||||
}
|
||||
|
||||
// recovered by HandleAccess
|
||||
mustPrintln := func(a ...any) {
|
||||
if _, _err := fmt.Fprintln(w, a...); _err != nil {
|
||||
|
||||
+17
-6
@@ -95,7 +95,7 @@ status : not in report
|
||||
var (
|
||||
cm *cache
|
||||
buf strings.Builder
|
||||
rp string
|
||||
r *rosa.Report
|
||||
)
|
||||
|
||||
if tc.status != nil || tc.report != "" {
|
||||
@@ -108,14 +108,25 @@ status : not in report
|
||||
}
|
||||
|
||||
if tc.report != "" {
|
||||
rp = filepath.Join(t.TempDir(), "report")
|
||||
if err := os.WriteFile(
|
||||
rp,
|
||||
pathname := filepath.Join(t.TempDir(), "report")
|
||||
err := os.WriteFile(
|
||||
pathname,
|
||||
unsafe.Slice(unsafe.StringData(tc.report), len(tc.report)),
|
||||
0400,
|
||||
); err != nil {
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r, err = rosa.OpenReport(pathname)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if err = r.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if tc.status != nil {
|
||||
@@ -157,7 +168,7 @@ status : not in report
|
||||
tc.args,
|
||||
&buf,
|
||||
cm != nil,
|
||||
rp,
|
||||
r,
|
||||
); !reflect.DeepEqual(err, wantErr) {
|
||||
t.Fatalf("commandInfo: error = %v, want %v", err, wantErr)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
// Package pkgserver implements the package metadata service backend.
|
||||
package pkgserver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"hakurei.app/internal/info"
|
||||
"hakurei.app/internal/rosa"
|
||||
)
|
||||
|
||||
// for lazy initialisation of serveInfo
|
||||
var (
|
||||
infoPayload struct {
|
||||
// Current package count.
|
||||
Count int `json:"count"`
|
||||
// Hakurei version, set at link time.
|
||||
HakureiVersion string `json:"hakurei_version"`
|
||||
}
|
||||
infoPayloadOnce sync.Once
|
||||
)
|
||||
|
||||
// handleInfo writes constant system information.
|
||||
func handleInfo(w http.ResponseWriter, _ *http.Request) {
|
||||
infoPayloadOnce.Do(func() {
|
||||
infoPayload.Count = int(rosa.PresetUnexportedStart)
|
||||
infoPayload.HakureiVersion = info.Version()
|
||||
})
|
||||
// TODO(mae): cache entire response if no additional fields are planned
|
||||
writeAPIPayload(w, infoPayload)
|
||||
}
|
||||
|
||||
// newStatusHandler returns a [http.HandlerFunc] that offers status files for
|
||||
// viewing or download, if available.
|
||||
func (index *packageIndex) newStatusHandler(disposition bool) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
m, ok := index.names[path.Base(r.URL.Path)]
|
||||
if !ok || !m.HasReport {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
contentType := "text/plain; charset=utf-8"
|
||||
if disposition {
|
||||
contentType = "application/octet-stream"
|
||||
|
||||
// quoting like this is unsound, but okay, because metadata is hardcoded
|
||||
contentDisposition := `attachment; filename="`
|
||||
contentDisposition += m.Name + "-"
|
||||
if m.Version != "" {
|
||||
contentDisposition += m.Version + "-"
|
||||
}
|
||||
contentDisposition += m.ids + `.log"`
|
||||
w.Header().Set("Content-Disposition", contentDisposition)
|
||||
}
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
if err := func() (err error) {
|
||||
defer index.handleAccess(&err)()
|
||||
_, err = w.Write(m.status)
|
||||
return
|
||||
}(); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(
|
||||
w, "cannot deliver status, contact maintainers",
|
||||
http.StatusInternalServerError,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleGet writes a slice of metadata with specified order.
|
||||
func (index *packageIndex) handleGet(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
limit, err := strconv.Atoi(q.Get("limit"))
|
||||
if err != nil || limit > 100 || limit < 1 {
|
||||
http.Error(
|
||||
w, "limit must be an integer between 1 and 100",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
i, err := strconv.Atoi(q.Get("index"))
|
||||
if err != nil || i >= len(index.sorts[0]) || i < 0 {
|
||||
http.Error(
|
||||
w, "index must be an integer between 0 and "+
|
||||
strconv.Itoa(int(rosa.PresetUnexportedStart-1)),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
sort, err := strconv.Atoi(q.Get("sort"))
|
||||
if err != nil || sort >= len(index.sorts) || sort < 0 {
|
||||
http.Error(
|
||||
w, "sort must be an integer between 0 and "+
|
||||
strconv.Itoa(sortOrderEnd),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
values := index.sorts[sort][i:min(i+limit, len(index.sorts[sort]))]
|
||||
writeAPIPayload(w, &struct {
|
||||
Values []*metadata `json:"values"`
|
||||
}{values})
|
||||
}
|
||||
|
||||
func (index *packageIndex) handleSearch(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
limit, err := strconv.Atoi(q.Get("limit"))
|
||||
if err != nil || limit > 100 || limit < 1 {
|
||||
http.Error(
|
||||
w, "limit must be an integer between 1 and 100",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
i, err := strconv.Atoi(q.Get("index"))
|
||||
if err != nil || i >= len(index.sorts[0]) || i < 0 {
|
||||
http.Error(
|
||||
w, "index must be an integer between 0 and "+
|
||||
strconv.Itoa(int(rosa.PresetUnexportedStart-1)),
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
search, err := url.QueryUnescape(q.Get("search"))
|
||||
if len(search) > 100 || err != nil {
|
||||
http.Error(
|
||||
w, "search must be a string between 0 and 100 characters long",
|
||||
http.StatusBadRequest,
|
||||
)
|
||||
return
|
||||
}
|
||||
desc := q.Get("desc") == "true"
|
||||
n, res, err := index.performSearchQuery(limit, i, search, desc)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
writeAPIPayload(w, &struct {
|
||||
Count int `json:"count"`
|
||||
Values []searchResult `json:"values"`
|
||||
}{n, res})
|
||||
}
|
||||
|
||||
// apiVersion is the name of the current API revision, as part of the pattern.
|
||||
const apiVersion = "v1"
|
||||
|
||||
// registerAPI registers API handler functions.
|
||||
func (index *packageIndex) registerAPI(mux *http.ServeMux) {
|
||||
mux.HandleFunc("GET /api/"+apiVersion+"/info", handleInfo)
|
||||
mux.HandleFunc("GET /api/"+apiVersion+"/get", index.handleGet)
|
||||
mux.HandleFunc("GET /api/"+apiVersion+"/search", index.handleSearch)
|
||||
mux.HandleFunc("GET /api/"+apiVersion+"/status/", index.newStatusHandler(false))
|
||||
mux.HandleFunc("GET /status/", index.newStatusHandler(true))
|
||||
}
|
||||
|
||||
// Register arranges for mux to service API requests.
|
||||
func Register(ctx context.Context, mux *http.ServeMux, report *rosa.Report) error {
|
||||
var index packageIndex
|
||||
index.search = make(searchCache)
|
||||
if err := index.populate(report); err != nil {
|
||||
return err
|
||||
}
|
||||
ticker := time.NewTicker(1 * time.Minute)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
ticker.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
index.search.clean()
|
||||
}
|
||||
}
|
||||
}()
|
||||
index.registerAPI(mux)
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeAPIPayload sets headers common to API responses and encodes payload as
|
||||
// JSON for the response body.
|
||||
func writeAPIPayload(w http.ResponseWriter, payload any) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
w.Header().Set("Pragma", "no-cache")
|
||||
w.Header().Set("Expires", "0")
|
||||
|
||||
if err := json.NewEncoder(w).Encode(payload); err != nil {
|
||||
log.Println(err)
|
||||
http.Error(
|
||||
w, "cannot encode payload, contact maintainers",
|
||||
http.StatusInternalServerError,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package pkgserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"slices"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/internal/info"
|
||||
"hakurei.app/internal/rosa"
|
||||
)
|
||||
|
||||
// prefix is prepended to every API path.
|
||||
const prefix = "/api/" + apiVersion + "/"
|
||||
|
||||
func TestAPIInfo(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
handleInfo(w, httptest.NewRequestWithContext(
|
||||
t.Context(),
|
||||
http.MethodGet,
|
||||
prefix+"info",
|
||||
nil,
|
||||
))
|
||||
|
||||
resp := w.Result()
|
||||
checkStatus(t, resp, http.StatusOK)
|
||||
checkAPIHeader(t, w.Header())
|
||||
|
||||
checkPayload(t, resp, struct {
|
||||
Count int `json:"count"`
|
||||
HakureiVersion string `json:"hakurei_version"`
|
||||
}{int(rosa.PresetUnexportedStart), info.Version()})
|
||||
}
|
||||
|
||||
func TestAPIGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
const target = prefix + "get"
|
||||
|
||||
index := newIndex(t)
|
||||
newRequest := func(suffix string) *httptest.ResponseRecorder {
|
||||
w := httptest.NewRecorder()
|
||||
index.handleGet(w, httptest.NewRequestWithContext(
|
||||
t.Context(),
|
||||
http.MethodGet,
|
||||
target+suffix,
|
||||
nil,
|
||||
))
|
||||
return w
|
||||
}
|
||||
|
||||
checkValidate := func(t *testing.T, suffix string, vmin, vmax int, wantErr string) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := newRequest("?" + suffix + "=invalid")
|
||||
resp := w.Result()
|
||||
checkError(t, resp, wantErr, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
t.Run("min", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := newRequest("?" + suffix + "=" + strconv.Itoa(vmin-1))
|
||||
resp := w.Result()
|
||||
checkError(t, resp, wantErr, http.StatusBadRequest)
|
||||
|
||||
w = newRequest("?" + suffix + "=" + strconv.Itoa(vmin))
|
||||
resp = w.Result()
|
||||
checkStatus(t, resp, http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("max", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := newRequest("?" + suffix + "=" + strconv.Itoa(vmax+1))
|
||||
resp := w.Result()
|
||||
checkError(t, resp, wantErr, http.StatusBadRequest)
|
||||
|
||||
w = newRequest("?" + suffix + "=" + strconv.Itoa(vmax))
|
||||
resp = w.Result()
|
||||
checkStatus(t, resp, http.StatusOK)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("limit", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
checkValidate(
|
||||
t, "index=0&sort=0&limit", 1, 100,
|
||||
"limit must be an integer between 1 and 100",
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("index", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
checkValidate(
|
||||
t, "limit=1&sort=0&index", 0, int(rosa.PresetUnexportedStart-1),
|
||||
"index must be an integer between 0 and "+strconv.Itoa(int(rosa.PresetUnexportedStart-1)),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("sort", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
checkValidate(
|
||||
t, "index=0&limit=1&sort", 0, int(sortOrderEnd),
|
||||
"sort must be an integer between 0 and "+strconv.Itoa(int(sortOrderEnd)),
|
||||
)
|
||||
})
|
||||
|
||||
checkWithSuffix := func(name, suffix string, want []*metadata) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
w := newRequest(suffix)
|
||||
resp := w.Result()
|
||||
checkStatus(t, resp, http.StatusOK)
|
||||
checkAPIHeader(t, w.Header())
|
||||
checkPayloadFunc(t, resp, func(got *struct {
|
||||
Values []*metadata `json:"values"`
|
||||
}) bool {
|
||||
return slices.EqualFunc(got.Values, want, func(a, b *metadata) bool {
|
||||
return (a.Version == b.Version ||
|
||||
a.Version == rosa.Unversioned ||
|
||||
b.Version == rosa.Unversioned) &&
|
||||
a.HasReport == b.HasReport &&
|
||||
a.Name == b.Name &&
|
||||
a.Description == b.Description &&
|
||||
a.Website == b.Website
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
checkWithSuffix("declarationAscending", "?limit=2&index=1&sort=0", []*metadata{
|
||||
{
|
||||
Metadata: rosa.GetMetadata(1),
|
||||
Version: rosa.Std.Version(1),
|
||||
},
|
||||
{
|
||||
Metadata: rosa.GetMetadata(2),
|
||||
Version: rosa.Std.Version(2),
|
||||
},
|
||||
})
|
||||
checkWithSuffix("declarationAscending offset", "?limit=3&index=5&sort=0", []*metadata{
|
||||
{
|
||||
Metadata: rosa.GetMetadata(5),
|
||||
Version: rosa.Std.Version(5),
|
||||
},
|
||||
{
|
||||
Metadata: rosa.GetMetadata(6),
|
||||
Version: rosa.Std.Version(6),
|
||||
},
|
||||
{
|
||||
Metadata: rosa.GetMetadata(7),
|
||||
Version: rosa.Std.Version(7),
|
||||
},
|
||||
})
|
||||
checkWithSuffix("declarationDescending", "?limit=3&index=0&sort=1", []*metadata{
|
||||
{
|
||||
Metadata: rosa.GetMetadata(rosa.PresetUnexportedStart - 1),
|
||||
Version: rosa.Std.Version(rosa.PresetUnexportedStart - 1),
|
||||
},
|
||||
{
|
||||
Metadata: rosa.GetMetadata(rosa.PresetUnexportedStart - 2),
|
||||
Version: rosa.Std.Version(rosa.PresetUnexportedStart - 2),
|
||||
},
|
||||
{
|
||||
Metadata: rosa.GetMetadata(rosa.PresetUnexportedStart - 3),
|
||||
Version: rosa.Std.Version(rosa.PresetUnexportedStart - 3),
|
||||
},
|
||||
})
|
||||
checkWithSuffix("declarationDescending offset", "?limit=1&index=37&sort=1", []*metadata{
|
||||
{
|
||||
Metadata: rosa.GetMetadata(rosa.PresetUnexportedStart - 38),
|
||||
Version: rosa.Std.Version(rosa.PresetUnexportedStart - 38),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package pkgserver
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
"hakurei.app/internal/rosa"
|
||||
)
|
||||
|
||||
const (
|
||||
declarationAscending = iota
|
||||
declarationDescending
|
||||
nameAscending
|
||||
nameDescending
|
||||
sizeAscending
|
||||
sizeDescending
|
||||
|
||||
sortOrderEnd = iota - 1
|
||||
)
|
||||
|
||||
// packageIndex refers to metadata by name and various sort orders.
|
||||
type packageIndex struct {
|
||||
sorts [sortOrderEnd + 1][rosa.PresetUnexportedStart]*metadata
|
||||
names map[string]*metadata
|
||||
search searchCache
|
||||
// Taken from [rosa.Report] if available.
|
||||
handleAccess func(*error) func()
|
||||
}
|
||||
|
||||
// metadata holds [rosa.Metadata] extended with additional information.
|
||||
type metadata struct {
|
||||
p rosa.PArtifact
|
||||
*rosa.Metadata
|
||||
|
||||
// Populated via [rosa.Toolchain.Version], [rosa.Unversioned] is equivalent
|
||||
// to the zero value. Otherwise, the zero value is invalid.
|
||||
Version string `json:"version,omitempty"`
|
||||
// Output data size, available if present in report.
|
||||
Size int64 `json:"size,omitempty"`
|
||||
// Whether the underlying [pkg.Artifact] is present in the report.
|
||||
HasReport bool `json:"report"`
|
||||
|
||||
// Ident string encoded ahead of time.
|
||||
ids string
|
||||
// Backed by [rosa.Report], access must be prepared by HandleAccess.
|
||||
status []byte
|
||||
}
|
||||
|
||||
// populate deterministically populates packageIndex, optionally with a report.
|
||||
func (index *packageIndex) populate(report *rosa.Report) (err error) {
|
||||
if report != nil {
|
||||
defer report.HandleAccess(&err)()
|
||||
index.handleAccess = report.HandleAccess
|
||||
}
|
||||
|
||||
var work [rosa.PresetUnexportedStart]*metadata
|
||||
index.names = make(map[string]*metadata)
|
||||
ir := pkg.NewIR()
|
||||
for p := range rosa.PresetUnexportedStart {
|
||||
m := metadata{
|
||||
p: p,
|
||||
|
||||
Metadata: rosa.GetMetadata(p),
|
||||
Version: rosa.Std.Version(p),
|
||||
}
|
||||
if m.Version == "" {
|
||||
return errors.New("invalid version from " + m.Name)
|
||||
}
|
||||
if m.Version == rosa.Unversioned {
|
||||
m.Version = ""
|
||||
}
|
||||
|
||||
if report != nil {
|
||||
id := ir.Ident(rosa.Std.Load(p))
|
||||
m.ids = pkg.Encode(id.Value())
|
||||
m.status, m.Size = report.ArtifactOf(id)
|
||||
m.HasReport = m.Size >= 0
|
||||
}
|
||||
|
||||
work[p] = &m
|
||||
index.names[m.Name] = &m
|
||||
}
|
||||
|
||||
index.sorts[declarationAscending] = work
|
||||
index.sorts[declarationDescending] = work
|
||||
slices.Reverse(index.sorts[declarationDescending][:])
|
||||
|
||||
index.sorts[nameAscending] = work
|
||||
slices.SortFunc(index.sorts[nameAscending][:], func(a, b *metadata) int {
|
||||
return strings.Compare(a.Name, b.Name)
|
||||
})
|
||||
index.sorts[nameDescending] = index.sorts[nameAscending]
|
||||
slices.Reverse(index.sorts[nameDescending][:])
|
||||
|
||||
index.sorts[sizeAscending] = work
|
||||
slices.SortFunc(index.sorts[sizeAscending][:], func(a, b *metadata) int {
|
||||
return cmp.Compare(a.Size, b.Size)
|
||||
})
|
||||
index.sorts[sizeDescending] = index.sorts[sizeAscending]
|
||||
slices.Reverse(index.sorts[sizeDescending][:])
|
||||
|
||||
return
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package pkgserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// newIndex returns the address of a newly populated packageIndex.
|
||||
func newIndex(t *testing.T) *packageIndex {
|
||||
t.Helper()
|
||||
|
||||
var index packageIndex
|
||||
if err := index.populate(nil); err != nil {
|
||||
t.Fatalf("populate: error = %v", err)
|
||||
}
|
||||
return &index
|
||||
}
|
||||
|
||||
// checkStatus checks response status code.
|
||||
func checkStatus(t *testing.T, resp *http.Response, want int) {
|
||||
t.Helper()
|
||||
|
||||
if resp.StatusCode != want {
|
||||
t.Errorf(
|
||||
"StatusCode: %s, want %s",
|
||||
http.StatusText(resp.StatusCode),
|
||||
http.StatusText(want),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// checkHeader checks the value of a header entry.
|
||||
func checkHeader(t *testing.T, h http.Header, key, want string) {
|
||||
t.Helper()
|
||||
|
||||
if got := h.Get(key); got != want {
|
||||
t.Errorf("%s: %q, want %q", key, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// checkAPIHeader checks common entries set for API endpoints.
|
||||
func checkAPIHeader(t *testing.T, h http.Header) {
|
||||
t.Helper()
|
||||
|
||||
checkHeader(t, h, "Content-Type", "application/json; charset=utf-8")
|
||||
checkHeader(t, h, "Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
checkHeader(t, h, "Pragma", "no-cache")
|
||||
checkHeader(t, h, "Expires", "0")
|
||||
}
|
||||
|
||||
// checkPayloadFunc checks the JSON response of an API endpoint by passing it to f.
|
||||
func checkPayloadFunc[T any](
|
||||
t *testing.T,
|
||||
resp *http.Response,
|
||||
f func(got *T) bool,
|
||||
) {
|
||||
t.Helper()
|
||||
|
||||
var got T
|
||||
r := io.Reader(resp.Body)
|
||||
if testing.Verbose() {
|
||||
var buf bytes.Buffer
|
||||
r = io.TeeReader(r, &buf)
|
||||
defer func() { t.Helper(); t.Log(buf.String()) }()
|
||||
}
|
||||
if err := json.NewDecoder(r).Decode(&got); err != nil {
|
||||
t.Fatalf("Decode: error = %v", err)
|
||||
}
|
||||
|
||||
if !f(&got) {
|
||||
t.Errorf("Body: %#v", got)
|
||||
}
|
||||
}
|
||||
|
||||
// checkPayload checks the JSON response of an API endpoint.
|
||||
func checkPayload[T any](t *testing.T, resp *http.Response, want T) {
|
||||
t.Helper()
|
||||
|
||||
checkPayloadFunc(t, resp, func(got *T) bool {
|
||||
return reflect.DeepEqual(got, &want)
|
||||
})
|
||||
}
|
||||
|
||||
func checkError(t *testing.T, resp *http.Response, error string, code int) {
|
||||
t.Helper()
|
||||
|
||||
checkStatus(t, resp, code)
|
||||
if got, _ := io.ReadAll(resp.Body); string(got) != fmt.Sprintln(error) {
|
||||
t.Errorf("Body: %q, want %q", string(got), error)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package pkgserver
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"maps"
|
||||
"regexp"
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
type searchCache map[string]searchCacheEntry
|
||||
type searchResult struct {
|
||||
NameIndices [][]int `json:"name_matches"`
|
||||
DescIndices [][]int `json:"desc_matches,omitempty"`
|
||||
Score float64 `json:"score"`
|
||||
*metadata
|
||||
}
|
||||
type searchCacheEntry struct {
|
||||
query string
|
||||
results []searchResult
|
||||
expiry time.Time
|
||||
}
|
||||
|
||||
func (index *packageIndex) performSearchQuery(limit int, i int, search string, desc bool) (int, []searchResult, error) {
|
||||
query := search
|
||||
if desc {
|
||||
query += ";withDesc"
|
||||
}
|
||||
entry, ok := index.search[query]
|
||||
if ok && len(entry.results) > 0 {
|
||||
return len(entry.results), entry.results[min(i, len(entry.results)-1):min(i+limit, len(entry.results))], nil
|
||||
}
|
||||
|
||||
regex, err := regexp.Compile(search)
|
||||
if err != nil {
|
||||
return 0, make([]searchResult, 0), err
|
||||
}
|
||||
res := make([]searchResult, 0)
|
||||
for p := range maps.Values(index.names) {
|
||||
nameIndices := regex.FindAllIndex([]byte(p.Name), -1)
|
||||
var descIndices [][]int = nil
|
||||
if desc {
|
||||
descIndices = regex.FindAllIndex([]byte(p.Description), -1)
|
||||
}
|
||||
if nameIndices == nil && descIndices == nil {
|
||||
continue
|
||||
}
|
||||
score := float64(indexsum(nameIndices)) / (float64(len(nameIndices)) + 1)
|
||||
if desc {
|
||||
score += float64(indexsum(descIndices)) / (float64(len(descIndices)) + 1) / 10.0
|
||||
}
|
||||
res = append(res, searchResult{
|
||||
NameIndices: nameIndices,
|
||||
DescIndices: descIndices,
|
||||
Score: score,
|
||||
metadata: p,
|
||||
})
|
||||
}
|
||||
slices.SortFunc(res[:], func(a, b searchResult) int { return -cmp.Compare(a.Score, b.Score) })
|
||||
expiry := time.Now().Add(1 * time.Minute)
|
||||
entry = searchCacheEntry{
|
||||
query: search,
|
||||
results: res,
|
||||
expiry: expiry,
|
||||
}
|
||||
index.search[query] = entry
|
||||
|
||||
return len(res), res[i:min(i+limit, len(entry.results))], nil
|
||||
}
|
||||
func (s *searchCache) clean() {
|
||||
maps.DeleteFunc(*s, func(_ string, v searchCacheEntry) bool {
|
||||
return v.expiry.Before(time.Now())
|
||||
})
|
||||
}
|
||||
func indexsum(in [][]int) int {
|
||||
sum := 0
|
||||
for i := 0; i < len(in); i++ {
|
||||
sum += in[i][1] - in[i][0]
|
||||
}
|
||||
return sum
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<title>Hakurei PkgServer</title>
|
||||
<script src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hakurei PkgServer</h1>
|
||||
<div class="top-controls" id="top-controls-regular">
|
||||
<p>Showing entries <span id="entry-counter"></span>.</p>
|
||||
<span id="search-bar">
|
||||
<label for="search">Search: </label>
|
||||
<input type="text" name="search" id="search"/>
|
||||
<button onclick="doSearch()">Find</button>
|
||||
<label for="include-desc">Include descriptions: </label>
|
||||
<input type="checkbox" name="include-desc" id="include-desc" checked/>
|
||||
</span>
|
||||
<div><label for="count">Entries per page: </label><select name="count" id="count">
|
||||
<option value="10">10</option>
|
||||
<option value="20">20</option>
|
||||
<option value="30">30</option>
|
||||
<option value="50">50</option>
|
||||
</select></div>
|
||||
<div><label for="sort">Sort by: </label><select name="sort" id="sort">
|
||||
<option value="0">Definition (ascending)</option>
|
||||
<option value="1">Definition (descending)</option>
|
||||
<option value="2">Name (ascending)</option>
|
||||
<option value="3">Name (descending)</option>
|
||||
<option value="4">Size (ascending)</option>
|
||||
<option value="5">Size (descending)</option>
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="top-controls" id="search-top-controls" hidden>
|
||||
<p>Showing search results <span id="search-entry-counter"></span> for query "<span id="search-query"></span>".</p>
|
||||
<button onclick="exitSearch()">Back</button>
|
||||
<div><label for="search-count">Entries per page: </label><select name="search-count" id="search-count">
|
||||
<option value="10">10</option>
|
||||
<option value="20">20</option>
|
||||
<option value="30">30</option>
|
||||
<option value="50">50</option>
|
||||
</select></div>
|
||||
<p>Sorted by best match</p>
|
||||
</div>
|
||||
<div class="page-controls"><a href="javascript:prevPage()">« Previous</a> <input type="text" class="page-number" value="1"/> <a href="javascript:nextPage()">Next »</a></div>
|
||||
<table id="pkg-list">
|
||||
<tr><td>Loading...</td></tr>
|
||||
</table>
|
||||
<div class="page-controls"><a href="javascript:prevPage()">« Previous</a> <input type="text" class="page-number" value="1"/> <a href="javascript:nextPage()">Next »</a></div>
|
||||
<footer>
|
||||
<p>©<a href="https://hakurei.app/">Hakurei</a> (<span id="hakurei-version">unknown</span>). Licensed under the MIT license.</p>
|
||||
</footer>
|
||||
<script>main();</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,331 @@
|
||||
interface PackageIndexEntry {
|
||||
name: string
|
||||
size?: number
|
||||
description?: string
|
||||
website?: string
|
||||
version?: string
|
||||
report?: boolean
|
||||
}
|
||||
|
||||
function entryToHTML(entry: PackageIndexEntry | SearchResult): HTMLTableRowElement {
|
||||
let v = entry.version != null ? `<span>${escapeHtml(entry.version)}</span>` : ""
|
||||
let s = entry.size != null && entry.size > 0 ? `<p>Size: ${toByteSizeString(entry.size)} (${entry.size})</p>` : ""
|
||||
let n: string
|
||||
let d: string
|
||||
if ('name_matches' in entry) {
|
||||
n = `<h2>${nameMatches(entry as SearchResult)} ${v}</h2>`
|
||||
} else {
|
||||
n = `<h2>${escapeHtml(entry.name)} ${v}</h2>`
|
||||
}
|
||||
if ('desc_matches' in entry && STATE.getIncludeDescriptions()) {
|
||||
d = descMatches(entry as SearchResult)
|
||||
} else {
|
||||
d = (entry as PackageIndexEntry).description != null ? `<p>${escapeHtml((entry as PackageIndexEntry).description)}</p>` : ""
|
||||
}
|
||||
let w = entry.website != null ? `<a href="${encodeURI(entry.website)}">Website</a>` : ""
|
||||
let r = entry.report ? `Log (<a href=\"${encodeURI('/api/v1/status/' + entry.name)}\">View</a> | <a href=\"${encodeURI('/status/' + entry.name)}\">Download</a>)` : ""
|
||||
let row = <HTMLTableRowElement>(document.createElement('tr'))
|
||||
row.innerHTML = `<td>
|
||||
${n}
|
||||
${d}
|
||||
${s}
|
||||
${w}
|
||||
${r}
|
||||
</td>`
|
||||
return row
|
||||
}
|
||||
|
||||
function nameMatches(sr: SearchResult): string {
|
||||
return markMatches(sr.name, sr.name_matches)
|
||||
}
|
||||
|
||||
function descMatches(sr: SearchResult): string {
|
||||
return markMatches(sr.description!, sr.desc_matches)
|
||||
}
|
||||
|
||||
function markMatches(str: string, indices: [number, number][]): string {
|
||||
if (indices == null) {
|
||||
return str
|
||||
}
|
||||
let out: string = ""
|
||||
let j = 0
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
if (j < indices.length) {
|
||||
if (i === indices[j][0]) {
|
||||
out += `<mark>${escapeHtmlChar(str[i])}`
|
||||
continue
|
||||
}
|
||||
if (i === indices[j][1]) {
|
||||
out += `</mark>${escapeHtmlChar(str[i])}`
|
||||
j++
|
||||
continue
|
||||
}
|
||||
}
|
||||
out += escapeHtmlChar(str[i])
|
||||
}
|
||||
if (indices[j] !== undefined) {
|
||||
out += "</mark>"
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function toByteSizeString(bytes: number): string {
|
||||
if (bytes == null) return `unspecified`
|
||||
if (bytes < 1024) return `${bytes}B`
|
||||
if (bytes < Math.pow(1024, 2)) return `${(bytes / 1024).toFixed(2)}kiB`
|
||||
if (bytes < Math.pow(1024, 3)) return `${(bytes / Math.pow(1024, 2)).toFixed(2)}MiB`
|
||||
if (bytes < Math.pow(1024, 4)) return `${(bytes / Math.pow(1024, 3)).toFixed(2)}GiB`
|
||||
if (bytes < Math.pow(1024, 5)) return `${(bytes / Math.pow(1024, 4)).toFixed(2)}TiB`
|
||||
return "not only is it big, it's large"
|
||||
}
|
||||
|
||||
const API_VERSION = 1
|
||||
const ENDPOINT = `/api/v${API_VERSION}`
|
||||
|
||||
interface InfoPayload {
|
||||
count?: number
|
||||
hakurei_version?: string
|
||||
}
|
||||
|
||||
async function infoRequest(): Promise<InfoPayload> {
|
||||
const res = await fetch(`${ENDPOINT}/info`)
|
||||
const payload = await res.json()
|
||||
return payload as InfoPayload
|
||||
}
|
||||
|
||||
interface GetPayload {
|
||||
values?: PackageIndexEntry[]
|
||||
}
|
||||
|
||||
enum SortOrders {
|
||||
DeclarationAscending,
|
||||
DeclarationDescending,
|
||||
NameAscending,
|
||||
NameDescending
|
||||
}
|
||||
|
||||
async function getRequest(limit: number, index: number, sort: SortOrders): Promise<GetPayload> {
|
||||
const res = await fetch(`${ENDPOINT}/get?limit=${limit}&index=${index}&sort=${sort.valueOf()}`)
|
||||
const payload = await res.json()
|
||||
return payload as GetPayload
|
||||
}
|
||||
|
||||
interface SearchResult extends PackageIndexEntry {
|
||||
name_matches: [number, number][]
|
||||
desc_matches: [number, number][]
|
||||
score: number
|
||||
}
|
||||
|
||||
interface SearchPayload {
|
||||
count?: number
|
||||
values?: SearchResult[]
|
||||
}
|
||||
|
||||
async function searchRequest(limit: number, index: number, search: string, desc: boolean): Promise<SearchPayload> {
|
||||
const res = await fetch(`${ENDPOINT}/search?limit=${limit}&index=${index}&search=${encodeURIComponent(search)}&desc=${desc}`)
|
||||
if (!res.ok) {
|
||||
exitSearch()
|
||||
alert("invalid search query!")
|
||||
return Promise.reject(res.statusText)
|
||||
}
|
||||
const payload = await res.json()
|
||||
return payload as SearchPayload
|
||||
}
|
||||
|
||||
class State {
|
||||
entriesPerPage: number = 10
|
||||
entryIndex: number = 0
|
||||
maxTotal: number = 0
|
||||
maxEntries: number = 0
|
||||
sort: SortOrders = SortOrders.DeclarationAscending
|
||||
search: boolean = false
|
||||
|
||||
getEntriesPerPage(): number {
|
||||
return this.entriesPerPage
|
||||
}
|
||||
|
||||
setEntriesPerPage(entriesPerPage: number) {
|
||||
this.entriesPerPage = entriesPerPage
|
||||
this.setEntryIndex(Math.floor(this.getEntryIndex() / entriesPerPage) * entriesPerPage)
|
||||
}
|
||||
|
||||
getEntryIndex(): number {
|
||||
return this.entryIndex
|
||||
}
|
||||
|
||||
setEntryIndex(entryIndex: number) {
|
||||
this.entryIndex = entryIndex
|
||||
this.updatePage()
|
||||
this.updateRange()
|
||||
this.updateListings()
|
||||
}
|
||||
|
||||
getMaxTotal(): number {
|
||||
return this.maxTotal
|
||||
}
|
||||
|
||||
setMaxTotal(max: number) {
|
||||
this.maxTotal = max
|
||||
}
|
||||
|
||||
getSortOrder(): SortOrders {
|
||||
return this.sort
|
||||
}
|
||||
|
||||
setSortOrder(sortOrder: SortOrders) {
|
||||
this.sort = sortOrder
|
||||
this.setEntryIndex(0)
|
||||
}
|
||||
|
||||
updatePage() {
|
||||
let page = Math.ceil(((this.getEntryIndex() + this.getEntriesPerPage()) - 1) / this.getEntriesPerPage())
|
||||
for (let e of document.getElementsByClassName("page-number")) {
|
||||
(e as HTMLInputElement).value = String(page)
|
||||
}
|
||||
}
|
||||
|
||||
updateRange() {
|
||||
let max = Math.min(this.getEntryIndex() + this.getEntriesPerPage(), this.getMaxTotal())
|
||||
document.getElementById("entry-counter")!.textContent = `${this.getEntryIndex() + 1}-${max} of ${this.getMaxTotal()}`
|
||||
if (this.search) {
|
||||
document.getElementById("search-entry-counter")!.textContent = `${this.getEntryIndex() + 1}-${max} of ${this.maxTotal}/${this.maxEntries}`
|
||||
document.getElementById("search-query")!.innerHTML = `<code>${escapeHtml(this.getSearchQuery())}</code>`
|
||||
}
|
||||
}
|
||||
|
||||
getSearchQuery(): string {
|
||||
let queryString = document.getElementById("search")!;
|
||||
return (queryString as HTMLInputElement).value
|
||||
}
|
||||
|
||||
getIncludeDescriptions(): boolean {
|
||||
let includeDesc = document.getElementById("include-desc")!;
|
||||
return (includeDesc as HTMLInputElement).checked
|
||||
}
|
||||
|
||||
updateListings() {
|
||||
if (this.search) {
|
||||
searchRequest(this.getEntriesPerPage(), this.getEntryIndex(), this.getSearchQuery(), this.getIncludeDescriptions())
|
||||
.then(res => {
|
||||
let table = document.getElementById("pkg-list")!
|
||||
table.innerHTML = ''
|
||||
for (let row of res.values!) {
|
||||
table.appendChild(entryToHTML(row))
|
||||
}
|
||||
STATE.maxTotal = res.count!
|
||||
STATE.updateRange()
|
||||
if(res.count! < 1) {
|
||||
exitSearch()
|
||||
alert("no results found!")
|
||||
}
|
||||
})
|
||||
} else {
|
||||
getRequest(this.getEntriesPerPage(), this.getEntryIndex(), this.getSortOrder())
|
||||
.then(res => {
|
||||
let table = document.getElementById("pkg-list")!
|
||||
table.innerHTML = ''
|
||||
for (let row of res.values!) {
|
||||
table.appendChild(entryToHTML(row))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let STATE: State
|
||||
|
||||
|
||||
function lastPageIndex(): number {
|
||||
return Math.floor(STATE.getMaxTotal() / STATE.getEntriesPerPage()) * STATE.getEntriesPerPage()
|
||||
}
|
||||
|
||||
function setPage(page: number) {
|
||||
STATE.setEntryIndex(Math.max(0, Math.min(STATE.getEntriesPerPage() * (page - 1), lastPageIndex())))
|
||||
}
|
||||
|
||||
|
||||
function escapeHtml(str?: string): string {
|
||||
let out: string = ''
|
||||
if (str == undefined) return ""
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
out += escapeHtmlChar(str[i])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function escapeHtmlChar(char: string): string {
|
||||
if (char.length != 1) return char
|
||||
switch (char[0]) {
|
||||
case '&':
|
||||
return "&"
|
||||
case '<':
|
||||
return "<"
|
||||
case '>':
|
||||
return ">"
|
||||
case '"':
|
||||
return """
|
||||
case "'":
|
||||
return "'"
|
||||
default:
|
||||
return char
|
||||
}
|
||||
}
|
||||
|
||||
function firstPage() {
|
||||
STATE.setEntryIndex(0)
|
||||
}
|
||||
|
||||
function prevPage() {
|
||||
let index = STATE.getEntryIndex()
|
||||
STATE.setEntryIndex(Math.max(0, index - STATE.getEntriesPerPage()))
|
||||
}
|
||||
|
||||
function lastPage() {
|
||||
STATE.setEntryIndex(lastPageIndex())
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
let index = STATE.getEntryIndex()
|
||||
STATE.setEntryIndex(Math.min(lastPageIndex(), index + STATE.getEntriesPerPage()))
|
||||
}
|
||||
|
||||
function doSearch() {
|
||||
document.getElementById("top-controls-regular")!.toggleAttribute("hidden");
|
||||
document.getElementById("search-top-controls")!.toggleAttribute("hidden");
|
||||
STATE.search = true;
|
||||
STATE.setEntryIndex(0);
|
||||
}
|
||||
|
||||
function exitSearch() {
|
||||
document.getElementById("top-controls-regular")!.toggleAttribute("hidden");
|
||||
document.getElementById("search-top-controls")!.toggleAttribute("hidden");
|
||||
STATE.search = false;
|
||||
STATE.setMaxTotal(STATE.maxEntries)
|
||||
STATE.setEntryIndex(0)
|
||||
}
|
||||
|
||||
function main() {
|
||||
STATE = new State()
|
||||
infoRequest()
|
||||
.then(res => {
|
||||
STATE.maxEntries = res.count!
|
||||
STATE.setMaxTotal(STATE.maxEntries)
|
||||
document.getElementById("hakurei-version")!.textContent = res.hakurei_version!
|
||||
STATE.updateRange()
|
||||
STATE.updateListings()
|
||||
})
|
||||
for (let e of document.getElementsByClassName("page-number")) {
|
||||
e.addEventListener("change", (_) => {
|
||||
setPage(parseInt((e as HTMLInputElement).value))
|
||||
})
|
||||
}
|
||||
document.getElementById("count")?.addEventListener("change", (event) => {
|
||||
STATE.setEntriesPerPage(parseInt((event.target as HTMLSelectElement).value))
|
||||
})
|
||||
document.getElementById("sort")?.addEventListener("change", (event) => {
|
||||
STATE.setSortOrder(parseInt((event.target as HTMLSelectElement).value))
|
||||
})
|
||||
document.getElementById("search")?.addEventListener("keyup", (event) => {
|
||||
if (event.key === 'Enter') doSearch()
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
.page-number {
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
.page-number {
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
background-color: #2c2c2c;
|
||||
color: ghostwhite;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
background-color: #d3d3d3;
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2024",
|
||||
"strict": true,
|
||||
"alwaysStrict": true,
|
||||
"outDir": "static"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Package ui holds the static web UI.
|
||||
package ui
|
||||
|
||||
import "net/http"
|
||||
|
||||
// Register arranges for mux to serve the embedded frontend.
|
||||
func Register(mux *http.ServeMux) {
|
||||
mux.Handle("GET /", http.FileServer(http.FS(static)))
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//go:build frontend
|
||||
|
||||
package ui
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
)
|
||||
|
||||
//go:generate tsc
|
||||
//go:generate cp index.html style.css static
|
||||
//go:embed static
|
||||
var _static embed.FS
|
||||
|
||||
var static = func() fs.FS {
|
||||
if f, err := fs.Sub(_static, "static"); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return f
|
||||
}
|
||||
}()
|
||||
@@ -0,0 +1,7 @@
|
||||
//go:build !frontend
|
||||
|
||||
package ui
|
||||
|
||||
import "testing/fstest"
|
||||
|
||||
var static fstest.MapFS
|
||||
+195
-6
@@ -20,6 +20,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
@@ -41,6 +42,9 @@ import (
|
||||
"hakurei.app/internal/pkg"
|
||||
"hakurei.app/internal/rosa"
|
||||
"hakurei.app/message"
|
||||
|
||||
"hakurei.app/cmd/mbf/internal/pkgserver"
|
||||
"hakurei.app/cmd/mbf/internal/pkgserver/ui"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -63,9 +67,13 @@ func main() {
|
||||
|
||||
var (
|
||||
flagQuiet bool
|
||||
flagQEMU bool
|
||||
flagArch string
|
||||
flagCheck bool
|
||||
flagLTO bool
|
||||
|
||||
flagCrossOverride int
|
||||
|
||||
addr net.UnixAddr
|
||||
)
|
||||
c := command.New(os.Stderr, log.Printf, "mbf", func([]string) error {
|
||||
@@ -89,13 +97,36 @@ func main() {
|
||||
if !flagLTO {
|
||||
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
|
||||
}).Flag(
|
||||
&flagQuiet,
|
||||
"q", command.BoolFlag(false),
|
||||
"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(
|
||||
&flagLTO,
|
||||
"lto", command.BoolFlag(false),
|
||||
@@ -104,6 +135,14 @@ func main() {
|
||||
&flagCheck,
|
||||
"check", command.BoolFlag(true),
|
||||
"Run test suites",
|
||||
).Flag(
|
||||
&flagCrossOverride,
|
||||
"cross-flags", command.IntFlag(-1),
|
||||
"Override non-native target preset flags",
|
||||
).Flag(
|
||||
&cm.verboseInit,
|
||||
"v", command.BoolFlag(false),
|
||||
"Do not suppress verbose output from init",
|
||||
).Flag(
|
||||
&cm.cures,
|
||||
"cures", command.IntFlag(0),
|
||||
@@ -136,7 +175,17 @@ func main() {
|
||||
c.NewCommand(
|
||||
"checksum", "Compute checksum of data read from standard input",
|
||||
func([]string) error {
|
||||
go func() { <-ctx.Done(); os.Exit(1) }()
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
os.Exit(1)
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
h := sha512.New384()
|
||||
if _, err := io.Copy(h, os.Stdin); err != nil {
|
||||
return err
|
||||
@@ -170,6 +219,7 @@ func main() {
|
||||
|
||||
{
|
||||
var (
|
||||
flagBind string
|
||||
flagStatus bool
|
||||
flagReport string
|
||||
)
|
||||
@@ -177,8 +227,52 @@ func main() {
|
||||
"info",
|
||||
"Display out-of-band metadata of an artifact",
|
||||
func(args []string) (err error) {
|
||||
return commandInfo(&cm, args, os.Stdout, flagStatus, flagReport)
|
||||
const shutdownTimeout = 15 * time.Second
|
||||
|
||||
var r *rosa.Report
|
||||
if flagReport != "" {
|
||||
if r, err = rosa.OpenReport(flagReport); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if closeErr := r.Close(); err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
defer r.HandleAccess(&err)()
|
||||
}
|
||||
|
||||
if flagBind == "" {
|
||||
return commandInfo(&cm, args, os.Stdout, flagStatus, r)
|
||||
}
|
||||
|
||||
var mux http.ServeMux
|
||||
ui.Register(&mux)
|
||||
if err = pkgserver.Register(ctx, &mux, r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
server := http.Server{Addr: flagBind, Handler: &mux}
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
cc, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
defer cancel()
|
||||
if _err := server.Shutdown(cc); _err != nil {
|
||||
log.Fatal(_err)
|
||||
}
|
||||
}()
|
||||
|
||||
msg.Verbosef("listening on %q", flagBind)
|
||||
err = server.ListenAndServe()
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
},
|
||||
).Flag(
|
||||
&flagBind,
|
||||
"bind", command.StringFlag(""),
|
||||
"TCP address for the server to listen on",
|
||||
).Flag(
|
||||
&flagStatus,
|
||||
"status", command.BoolFlag(false),
|
||||
@@ -412,6 +506,11 @@ func main() {
|
||||
flagExport string
|
||||
flagRemote bool
|
||||
flagNoReply bool
|
||||
flagFaults bool
|
||||
flagPop bool
|
||||
|
||||
flagBoot bool
|
||||
flagStd bool
|
||||
)
|
||||
c.NewCommand(
|
||||
"cure",
|
||||
@@ -425,11 +524,18 @@ func main() {
|
||||
return fmt.Errorf("unknown artifact %q", args[0])
|
||||
}
|
||||
|
||||
t := rosa.Std
|
||||
if flagBoot {
|
||||
t -= 2
|
||||
} else if flagStd {
|
||||
t -= 1
|
||||
}
|
||||
|
||||
switch {
|
||||
default:
|
||||
var pathname *check.Absolute
|
||||
err := cm.Do(func(cache *pkg.Cache) (err error) {
|
||||
pathname, _, err = cache.Cure(rosa.Std.Load(p))
|
||||
pathname, _, err = cache.Cure(t.Load(p))
|
||||
return
|
||||
})
|
||||
if err != nil {
|
||||
@@ -482,7 +588,7 @@ func main() {
|
||||
return cm.Do(func(cache *pkg.Cache) error {
|
||||
return cache.EnterExec(
|
||||
ctx,
|
||||
rosa.Std.Load(p),
|
||||
t.Load(p),
|
||||
true, os.Stdin, os.Stdout, os.Stderr,
|
||||
rosa.AbsSystem.Append("bin", "mksh"),
|
||||
"sh",
|
||||
@@ -494,7 +600,7 @@ func main() {
|
||||
if flagNoReply {
|
||||
flags |= remoteNoReply
|
||||
}
|
||||
a := rosa.Std.Load(p)
|
||||
a := t.Load(p)
|
||||
pathname, err := cureRemote(ctx, &addr, a, flags)
|
||||
if !flagNoReply && err == nil {
|
||||
log.Println(pathname)
|
||||
@@ -510,6 +616,49 @@ func main() {
|
||||
}
|
||||
|
||||
return err
|
||||
|
||||
case flagFaults:
|
||||
var faults []pkg.Fault
|
||||
if err := cm.Do(func(cache *pkg.Cache) (err error) {
|
||||
faults, err = cache.ReadFaults(t.Load(p))
|
||||
return
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fault := range faults {
|
||||
log.Printf("%s: %s ago", fault.String(), time.Since(fault.Time()))
|
||||
}
|
||||
return nil
|
||||
|
||||
case flagPop:
|
||||
var faults []pkg.Fault
|
||||
if err := cm.Do(func(cache *pkg.Cache) (err error) {
|
||||
faults, err = cache.ReadFaults(t.Load(p))
|
||||
return
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(faults) == 0 {
|
||||
return errors.New("no fault entries found")
|
||||
}
|
||||
fault := faults[len(faults)-1]
|
||||
r, err := fault.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err = io.Copy(os.Stdout, r); err != nil {
|
||||
_ = r.Close()
|
||||
return err
|
||||
}
|
||||
fmt.Println()
|
||||
if err = r.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("faulting cure terminated %s ago", time.Since(fault.Time()))
|
||||
return fault.Destroy()
|
||||
}
|
||||
},
|
||||
).Flag(
|
||||
@@ -532,9 +681,48 @@ func main() {
|
||||
&flagNoReply,
|
||||
"no-reply", command.BoolFlag(false),
|
||||
"Do not receive a reply from the daemon",
|
||||
).Flag(
|
||||
&flagBoot,
|
||||
"boot", command.BoolFlag(false),
|
||||
"Build on the stage0 toolchain",
|
||||
).Flag(
|
||||
&flagStd,
|
||||
"std", command.BoolFlag(false),
|
||||
"Build on the intermediate toolchain",
|
||||
).Flag(
|
||||
&flagFaults,
|
||||
"faults", command.BoolFlag(false),
|
||||
"Display fault entries of the specified artifact",
|
||||
).Flag(
|
||||
&flagPop,
|
||||
"pop", command.BoolFlag(false),
|
||||
"Display and destroy the most recent fault entry",
|
||||
)
|
||||
}
|
||||
|
||||
c.NewCommand(
|
||||
"clear",
|
||||
"Remove all fault entries from the cache",
|
||||
func([]string) error {
|
||||
return cm.Do(func(*pkg.Cache) error {
|
||||
pathname := filepath.Join(cm.base, "fault")
|
||||
dents, err := os.ReadDir(pathname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, dent := range dents {
|
||||
msg.Verbosef("destroying entry %s", dent.Name())
|
||||
if err = os.Remove(filepath.Join(pathname, dent.Name())); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.Printf("destroyed %d fault entries", len(dents))
|
||||
return nil
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
c.NewCommand(
|
||||
"abort",
|
||||
"Abort all pending cures on the daemon",
|
||||
@@ -633,6 +821,7 @@ func main() {
|
||||
z.Hostname = "localhost"
|
||||
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
|
||||
z.Stdin, z.Stdout, z.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
z.Quiet = !cm.verboseInit
|
||||
if s, ok := os.LookupEnv("TERM"); ok {
|
||||
z.Env = append(z.Env, "TERM="+s)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/internal/rosa"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
rosa.DropCaches("", rosa.OptLLVMNoLTO)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestCureAll(t *testing.T) {
|
||||
t.Parallel()
|
||||
const env = "ROSA_TEST_DAEMON"
|
||||
|
||||
if !testing.Verbose() {
|
||||
t.Skip("verbose flag not set")
|
||||
}
|
||||
|
||||
pathname, ok := os.LookupEnv(env)
|
||||
if !ok {
|
||||
t.Skip(env + " not set")
|
||||
}
|
||||
|
||||
addr := net.UnixAddr{Net: "unix", Name: pathname}
|
||||
t.Cleanup(func() {
|
||||
if t.Failed() {
|
||||
if err := abortRemote(t.Context(), &addr, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for i := range rosa.PresetEnd {
|
||||
p := rosa.PArtifact(i)
|
||||
t.Run(rosa.GetMetadata(p).Name, func(t *testing.T) {
|
||||
_, err := cureRemote(t.Context(), &addr, rosa.Std.Load(p), 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,14 @@
|
||||
};
|
||||
|
||||
virtualisation = {
|
||||
# Hopefully reduces spurious test failures:
|
||||
memorySize = if pkgs.stdenv.hostPlatform.is32bit then 2046 else 8192;
|
||||
|
||||
diskSize = 6 * 1024;
|
||||
|
||||
qemu.options = [
|
||||
# Increase test performance:
|
||||
"-smp 8"
|
||||
"-smp 16"
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ testers.nixosTest {
|
||||
# For go tests:
|
||||
(pkgs.writeShellScriptBin "sharefs-workload-hakurei-tests" ''
|
||||
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 ./...'
|
||||
'')
|
||||
];
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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_NET_ADMIN = 0xc
|
||||
CAP_DAC_OVERRIDE = 0x1
|
||||
CAP_SETFCAP = 0x1f
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
+27
-6
@@ -67,6 +67,9 @@ type (
|
||||
// Copied to the underlying [exec.Cmd].
|
||||
WaitDelay time.Duration
|
||||
|
||||
// Suppress verbose output of init.
|
||||
Quiet bool
|
||||
|
||||
cmd *exec.Cmd
|
||||
ctx context.Context
|
||||
msg message.Msg
|
||||
@@ -88,12 +91,20 @@ type (
|
||||
// Time to wait for processes lingering after the initial process terminates.
|
||||
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.
|
||||
Uid int
|
||||
// Mapped Gid in user namespace.
|
||||
Gid int
|
||||
// Hostname value in UTS namespace.
|
||||
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.
|
||||
*Ops
|
||||
|
||||
@@ -213,6 +224,9 @@ func (p *Container) Start() error {
|
||||
if p.cmd.Process != nil {
|
||||
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 {
|
||||
return err
|
||||
@@ -283,6 +297,18 @@ func (p *Container) Start() error {
|
||||
if !p.HostNet {
|
||||
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
|
||||
if r, w, err := os.Pipe(); err != nil {
|
||||
@@ -342,8 +368,6 @@ func (p *Container) Start() error {
|
||||
Err: ENOSYS,
|
||||
Origin: true,
|
||||
}
|
||||
} else {
|
||||
p.msg.Verbosef("landlock abi version %d", abi)
|
||||
}
|
||||
|
||||
if rulesetFd, err := rulesetAttr.Create(0); err != nil {
|
||||
@@ -353,7 +377,6 @@ func (p *Container) Start() error {
|
||||
Err: err,
|
||||
}
|
||||
} else {
|
||||
p.msg.Verbosef("enforcing landlock ruleset %s", rulesetAttr)
|
||||
if err = landlock.RestrictSelf(rulesetFd, 0); err != nil {
|
||||
_ = Close(rulesetFd)
|
||||
return &StartError{
|
||||
@@ -410,7 +433,6 @@ func (p *Container) Start() error {
|
||||
}
|
||||
}
|
||||
|
||||
p.msg.Verbose("starting container init")
|
||||
if err := p.cmd.Start(); err != nil {
|
||||
return &StartError{
|
||||
Step: "start container init",
|
||||
@@ -481,7 +503,6 @@ func (p *Container) Serve() (err error) {
|
||||
}
|
||||
|
||||
case <-done:
|
||||
p.msg.Verbose("setup payload took", time.Since(t))
|
||||
return
|
||||
}
|
||||
}(p.setup[1])
|
||||
@@ -491,7 +512,7 @@ func (p *Container) Serve() (err error) {
|
||||
Getuid(),
|
||||
Getgid(),
|
||||
len(p.ExtraFiles),
|
||||
p.msg.IsVerbose(),
|
||||
p.msg.IsVerbose() && !p.Quiet,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+203
-81
@@ -16,6 +16,8 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/command"
|
||||
@@ -233,6 +235,9 @@ func earlyMnt(mnt ...*vfs.MountInfoEntry) func(*testing.T, context.Context) []*v
|
||||
return func(*testing.T, context.Context) []*vfs.MountInfoEntry { return mnt }
|
||||
}
|
||||
|
||||
//go:linkname toHost hakurei.app/container.toHost
|
||||
func toHost(name string) string
|
||||
|
||||
var containerTestCases = []struct {
|
||||
name string
|
||||
filter bool
|
||||
@@ -332,13 +337,15 @@ var containerTestCases = []struct {
|
||||
func(t *testing.T, ctx context.Context) []*vfs.MountInfoEntry {
|
||||
return []*vfs.MountInfoEntry{
|
||||
ent("/", hst.PrivateTmp, "rw", "overlay", "overlay",
|
||||
"rw,lowerdir="+
|
||||
container.InternalToHostOvlEscape(ctx.Value(testVal("lower0")).(*check.Absolute).String())+":"+
|
||||
container.InternalToHostOvlEscape(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
|
||||
"rw"+
|
||||
",lowerdir+="+
|
||||
toHost(ctx.Value(testVal("lower0")).(*check.Absolute).String())+
|
||||
",lowerdir+="+
|
||||
toHost(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
|
||||
",upperdir="+
|
||||
container.InternalToHostOvlEscape(ctx.Value(testVal("upper")).(*check.Absolute).String())+
|
||||
toHost(ctx.Value(testVal("upper")).(*check.Absolute).String())+
|
||||
",workdir="+
|
||||
container.InternalToHostOvlEscape(ctx.Value(testVal("work")).(*check.Absolute).String())+
|
||||
toHost(ctx.Value(testVal("work")).(*check.Absolute).String())+
|
||||
",redirect_dir=nofollow,uuid=on,userxattr"),
|
||||
}
|
||||
},
|
||||
@@ -388,9 +395,11 @@ var containerTestCases = []struct {
|
||||
func(t *testing.T, ctx context.Context) []*vfs.MountInfoEntry {
|
||||
return []*vfs.MountInfoEntry{
|
||||
ent("/", hst.PrivateTmp, "rw", "overlay", "overlay",
|
||||
"ro,lowerdir="+
|
||||
container.InternalToHostOvlEscape(ctx.Value(testVal("lower0")).(*check.Absolute).String())+":"+
|
||||
container.InternalToHostOvlEscape(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
|
||||
"ro"+
|
||||
",lowerdir+="+
|
||||
toHost(ctx.Value(testVal("lower0")).(*check.Absolute).String())+
|
||||
",lowerdir+="+
|
||||
toHost(ctx.Value(testVal("lower1")).(*check.Absolute).String())+
|
||||
",redirect_dir=nofollow,userxattr"),
|
||||
}
|
||||
},
|
||||
@@ -400,39 +409,11 @@ var containerTestCases = []struct {
|
||||
func TestContainer(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
t.Run("cancel", testContainerCancel(nil, func(t *testing.T, c *container.Container) {
|
||||
wantErr := context.Canceled
|
||||
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)
|
||||
}
|
||||
}))
|
||||
|
||||
var suffix string
|
||||
runTests:
|
||||
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()
|
||||
|
||||
wantOps, wantOpsCtx := tc.ops(t)
|
||||
@@ -456,6 +437,8 @@ func TestContainer(t *testing.T) {
|
||||
c.SeccompDisable = !tc.filter
|
||||
c.RetainSession = tc.session
|
||||
c.HostNet = tc.net
|
||||
c.InitAsRoot = _suffix != ""
|
||||
c.Env = append(c.Env, "HAKUREI_TEST_SUFFIX="+_suffix)
|
||||
if info.CanDegrade {
|
||||
if _, err := landlock.GetABI(); err != nil {
|
||||
if !errors.Is(err, syscall.ENOSYS) {
|
||||
@@ -465,6 +448,9 @@ func TestContainer(t *testing.T) {
|
||||
t.Log("Landlock LSM is unavailable, enabling HostAbstract")
|
||||
}
|
||||
}
|
||||
if c.InitAsRoot {
|
||||
c.SeccompPresets &= ^std.PresetDenyNS
|
||||
}
|
||||
|
||||
c.
|
||||
Readonly(check.MustAbs(pathReadonly), 0755).
|
||||
@@ -533,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 {
|
||||
@@ -555,49 +546,118 @@ func hostnameFromTestCase(name string) string {
|
||||
}
|
||||
|
||||
func testContainerCancel(
|
||||
t *testing.T,
|
||||
containerExtra func(c *container.Container),
|
||||
waitCheck func(t *testing.T, c *container.Container),
|
||||
) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
waitCheck func(ps *os.ProcessState, waitErr error),
|
||||
) {
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
|
||||
c := helperNewContainer(ctx, "block")
|
||||
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
||||
if containerExtra != nil {
|
||||
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)
|
||||
c := helperNewContainer(ctx, "block")
|
||||
c.Stdout, c.Stderr = os.Stdout, os.Stderr
|
||||
if containerExtra != nil {
|
||||
containerExtra(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) {
|
||||
@@ -633,6 +693,8 @@ func init() {
|
||||
})
|
||||
|
||||
c.Command("container", command.UsageInternal, func(args []string) error {
|
||||
asRoot := os.Getenv("HAKUREI_TEST_SUFFIX") == " as root"
|
||||
|
||||
if len(args) != 1 {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
@@ -650,6 +712,66 @@ func init() {
|
||||
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)
|
||||
if host, err := os.Hostname(); err != nil {
|
||||
return fmt.Errorf("cannot get hostname: %v", err)
|
||||
@@ -767,7 +889,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
c.MustParse(os.Args[1:], func(err error) {
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
log.Fatal(err)
|
||||
}
|
||||
})
|
||||
return
|
||||
|
||||
@@ -65,6 +65,8 @@ type syscallDispatcher interface {
|
||||
remount(msg message.Msg, target string, flags uintptr) error
|
||||
// mountTmpfs provides mountTmpfs.
|
||||
mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error
|
||||
// mountOverlay provides mountOverlay.
|
||||
mountOverlay(target string, options [][2]string) error
|
||||
// ensureFile provides ensureFile.
|
||||
ensureFile(name string, perm, pperm os.FileMode) error
|
||||
// mustLoopback provides mustLoopback.
|
||||
@@ -169,6 +171,9 @@ func (direct) remount(msg message.Msg, target string, flags uintptr) error {
|
||||
func (k direct) mountTmpfs(fsname, target string, flags uintptr, size int, perm os.FileMode) error {
|
||||
return mountTmpfs(k, fsname, target, flags, size, perm)
|
||||
}
|
||||
func (k direct) mountOverlay(target string, options [][2]string) error {
|
||||
return mountOverlay(target, options)
|
||||
}
|
||||
func (direct) ensureFile(name string, perm, pperm os.FileMode) error {
|
||||
return ensureFile(name, perm, pperm)
|
||||
}
|
||||
|
||||
@@ -468,6 +468,14 @@ func (k *kstub) mountTmpfs(fsname, target string, flags uintptr, size int, perm
|
||||
stub.CheckArg(k.Stub, "perm", perm, 4))
|
||||
}
|
||||
|
||||
func (k *kstub) mountOverlay(target string, options [][2]string) error {
|
||||
k.Helper()
|
||||
return k.Expects("mountOverlay").Error(
|
||||
stub.CheckArg(k.Stub, "target", target, 0),
|
||||
stub.CheckArgReflect(k.Stub, "options", options, 1),
|
||||
)
|
||||
}
|
||||
|
||||
func (k *kstub) ensureFile(name string, perm, pperm os.FileMode) error {
|
||||
k.Helper()
|
||||
return k.Expects("ensureFile").Error(
|
||||
|
||||
@@ -118,6 +118,10 @@ func errnoFallback(op, path string, err error) (syscall.Errno, *os.PathError) {
|
||||
|
||||
// mount wraps syscall.Mount for error handling.
|
||||
func mount(source, target, fstype string, flags uintptr, data string) error {
|
||||
if max(len(source), len(target), len(data))+1 > os.Getpagesize() {
|
||||
return &MountError{source, target, fstype, flags, data, syscall.ENOMEM}
|
||||
}
|
||||
|
||||
err := syscall.Mount(source, target, fstype, flags, data)
|
||||
if err == nil {
|
||||
return nil
|
||||
|
||||
+106
-23
@@ -11,11 +11,13 @@ import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
. "syscall"
|
||||
"time"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/container/seccomp"
|
||||
"hakurei.app/ext"
|
||||
"hakurei.app/fhs"
|
||||
@@ -182,23 +184,33 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
||||
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
|
||||
if err := k.setDumpable(ext.SUID_DUMP_USER); err != nil {
|
||||
k.fatalf(msg, "cannot set SUID_DUMP_USER: %v", err)
|
||||
}
|
||||
if err := k.writeFile(fhs.Proc+"self/uid_map",
|
||||
append([]byte{}, strconv.Itoa(param.Uid)+" "+strconv.Itoa(param.HostUid)+" 1\n"...),
|
||||
0); err != nil {
|
||||
if err := k.writeFile(
|
||||
fhs.Proc+"self/uid_map",
|
||||
[]byte(strconv.Itoa(uid)+" "+strconv.Itoa(param.HostUid)+" 1\n"),
|
||||
0,
|
||||
); err != nil {
|
||||
k.fatalf(msg, "%v", err)
|
||||
}
|
||||
if err := k.writeFile(fhs.Proc+"self/setgroups",
|
||||
if err := k.writeFile(
|
||||
fhs.Proc+"self/setgroups",
|
||||
[]byte("deny\n"),
|
||||
0); err != nil && !os.IsNotExist(err) {
|
||||
0,
|
||||
); err != nil && !os.IsNotExist(err) {
|
||||
k.fatalf(msg, "%v", err)
|
||||
}
|
||||
if err := k.writeFile(fhs.Proc+"self/gid_map",
|
||||
append([]byte{}, strconv.Itoa(param.Gid)+" "+strconv.Itoa(param.HostGid)+" 1\n"...),
|
||||
0); err != nil {
|
||||
[]byte(strconv.Itoa(gid)+" "+strconv.Itoa(param.HostGid)+" 1\n"),
|
||||
0,
|
||||
); err != nil {
|
||||
k.fatalf(msg, "%v", err)
|
||||
}
|
||||
if err := k.setDumpable(ext.SUID_DUMP_DISABLE); err != nil {
|
||||
@@ -223,6 +235,23 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
||||
state := &setupState{process: make(map[int]WaitStatus), Params: ¶m.Params, Msg: msg, Context: ctx}
|
||||
defer cancel()
|
||||
|
||||
if err := k.mount(SourceTmpfsRootfs, intermediateHostPath, FstypeTmpfs, MS_NODEV|MS_NOSUID, zeroString); err != nil {
|
||||
k.fatalf(msg, "cannot mount intermediate root: %v", optionalErrorUnwrap(err))
|
||||
}
|
||||
if err := k.chdir(intermediateHostPath); err != nil {
|
||||
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;
|
||||
this step is mostly for gathering information that would otherwise be
|
||||
difficult to obtain via library functions after pivot_root, and
|
||||
@@ -242,13 +271,6 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
||||
}
|
||||
}
|
||||
|
||||
if err := k.mount(SourceTmpfsRootfs, intermediateHostPath, FstypeTmpfs, MS_NODEV|MS_NOSUID, zeroString); err != nil {
|
||||
k.fatalf(msg, "cannot mount intermediate root: %v", optionalErrorUnwrap(err))
|
||||
}
|
||||
if err := k.chdir(intermediateHostPath); err != nil {
|
||||
k.fatalf(msg, "cannot enter intermediate host path: %v", err)
|
||||
}
|
||||
|
||||
if err := k.mkdir(sysrootDir, 0755); err != nil {
|
||||
k.fatalf(msg, "%v", err)
|
||||
}
|
||||
@@ -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
|
||||
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))
|
||||
@@ -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 {
|
||||
k.fatalf(msg, "cannot clear the ambient capability set: %v", err)
|
||||
}
|
||||
for i := uintptr(0); i <= lastcap; i++ {
|
||||
if param.Privileged && i == CAP_SYS_ADMIN {
|
||||
for i := range lastcap + 1 {
|
||||
if slices.Contains(keepCaps, i) {
|
||||
continue
|
||||
}
|
||||
if err := k.capBoundingSetDrop(i); err != nil {
|
||||
@@ -336,20 +408,23 @@ func initEntrypoint(k syscallDispatcher, msg message.Msg) {
|
||||
}
|
||||
|
||||
var keep [2]uint32
|
||||
if param.Privileged {
|
||||
keep[capToIndex(CAP_SYS_ADMIN)] |= capToMask(CAP_SYS_ADMIN)
|
||||
|
||||
if err := k.capAmbientRaise(CAP_SYS_ADMIN); err != nil {
|
||||
k.fatalf(msg, "cannot raise CAP_SYS_ADMIN: %v", err)
|
||||
}
|
||||
for _, c := range keepCaps {
|
||||
keep[capToIndex(c)] |= capToMask(c)
|
||||
}
|
||||
|
||||
if err := k.capset(
|
||||
&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 {
|
||||
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 {
|
||||
rules := param.SeccompRules
|
||||
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.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)
|
||||
if err := k.start(cmd); err != nil {
|
||||
k.fatalf(msg, "%v", err)
|
||||
|
||||
+73
-73
@@ -332,6 +332,8 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("fatalf", stub.ExpectArgs{"invalid op at index %d", []any{0}}, nil, nil),
|
||||
/* end early */
|
||||
@@ -370,6 +372,8 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("fatalf", stub.ExpectArgs{"invalid op at index %d", []any{0}}, nil, nil),
|
||||
/* end early */
|
||||
@@ -408,6 +412,8 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", stub.UniqueError(61)),
|
||||
call("fatalf", stub.ExpectArgs{"cannot prepare op at index %d: %v", []any{0, stub.UniqueError(61)}}, nil, nil),
|
||||
@@ -447,6 +453,8 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", &os.PathError{Op: "readlink", Path: "/", Err: stub.UniqueError(60)}),
|
||||
call("fatal", stub.ExpectArgs{[]any{"cannot readlink /: unique error 60 injected by the test suite"}}, nil, nil),
|
||||
@@ -486,9 +494,6 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, stub.UniqueError(58)),
|
||||
call("fatalf", stub.ExpectArgs{"cannot mount intermediate root: %v", []any{stub.UniqueError(58)}}, nil, nil),
|
||||
},
|
||||
@@ -526,9 +531,6 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, stub.UniqueError(56)),
|
||||
call("fatalf", stub.ExpectArgs{"cannot enter intermediate host path: %v", []any{stub.UniqueError(56)}}, nil, nil),
|
||||
@@ -567,11 +569,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, stub.UniqueError(54)),
|
||||
call("fatalf", stub.ExpectArgs{"%v", []any{stub.UniqueError(54)}}, nil, nil),
|
||||
},
|
||||
@@ -609,11 +611,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, stub.UniqueError(52)),
|
||||
call("fatalf", stub.ExpectArgs{"cannot bind sysroot: %v", []any{stub.UniqueError(52)}}, nil, nil),
|
||||
@@ -652,11 +654,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, stub.UniqueError(50)),
|
||||
@@ -696,11 +698,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -741,11 +743,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -787,11 +789,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -842,11 +844,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -897,11 +899,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -953,11 +955,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1010,11 +1012,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1069,11 +1071,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1129,11 +1131,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1190,11 +1192,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1252,11 +1254,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1315,11 +1317,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1379,11 +1381,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1444,11 +1446,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1510,11 +1512,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1584,11 +1586,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1622,7 +1624,6 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, 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(0xa)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||
@@ -1654,8 +1655,9 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, 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("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},
|
||||
|
||||
@@ -1691,11 +1693,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1729,7 +1731,6 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, 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(0xa)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||
@@ -1761,8 +1762,7 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, 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{{0, 0x200000, 0x200000}, {0, 0, 0}}}, nil, stub.UniqueError(17)),
|
||||
call("capset", stub.ExpectArgs{&capHeader{_LINUX_CAPABILITY_VERSION_3, 0}, &[2]capData{{0x200100, 0x200100, 0x200100}, {0, 0, 0}}}, nil, stub.UniqueError(17)),
|
||||
call("fatalf", stub.ExpectArgs{"cannot capset: %v", []any{stub.UniqueError(17)}}, nil, nil),
|
||||
},
|
||||
}, nil},
|
||||
@@ -1799,11 +1799,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -1837,7 +1837,6 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, 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(0xa)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||
@@ -1869,8 +1868,9 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, 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("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("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),
|
||||
@@ -1908,11 +1908,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2032,11 +2032,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(4), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2132,11 +2132,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(4), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2232,11 +2232,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(4), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2323,11 +2323,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(4), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2418,11 +2418,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(4), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2520,11 +2520,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2659,11 +2659,11 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("sethostname", stub.ExpectArgs{[]byte("hakurei-check")}, nil, nil),
|
||||
call("lastcap", stub.ExpectArgs{}, uintptr(40), nil),
|
||||
call("mount", stub.ExpectArgs{"", "/", "", uintptr(0x8c000), ""}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
/* begin early */
|
||||
call("evalSymlinks", stub.ExpectArgs{"/"}, "/", nil),
|
||||
/* end early */
|
||||
call("mount", stub.ExpectArgs{"rootfs", "/proc/self/fd", "tmpfs", uintptr(6), ""}, nil, nil),
|
||||
call("chdir", stub.ExpectArgs{"/proc/self/fd"}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"sysroot", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"sysroot", "sysroot", "", uintptr(0xd000), ""}, nil, nil),
|
||||
call("mkdir", stub.ExpectArgs{"host", os.FileMode(0755)}, nil, nil),
|
||||
@@ -2697,7 +2697,6 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x5)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x6)}, 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(0xa)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0xb)}, nil, nil),
|
||||
@@ -2729,8 +2728,9 @@ func TestInitEntrypoint(t *testing.T) {
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x26)}, nil, nil),
|
||||
call("capBoundingSetDrop", stub.ExpectArgs{uintptr(0x27)}, 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("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("seccompLoad", stub.ExpectArgs{seccomp.Preset(0xf, 0), seccomp.ExportFlag(0)}, nil, nil),
|
||||
call("verbosef", stub.ExpectArgs{"%d filter rules loaded", []any{73}}, nil, nil),
|
||||
|
||||
+40
-12
@@ -4,9 +4,9 @@ import (
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/ext"
|
||||
"hakurei.app/fhs"
|
||||
)
|
||||
|
||||
@@ -150,7 +150,7 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error {
|
||||
if v, err := k.evalSymlinks(o.Upper.String()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
o.upper = check.EscapeOverlayDataSegment(toHost(v))
|
||||
o.upper = toHost(v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error {
|
||||
if v, err := k.evalSymlinks(o.Work.String()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
o.work = check.EscapeOverlayDataSegment(toHost(v))
|
||||
o.work = toHost(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,12 +168,39 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error {
|
||||
if v, err := k.evalSymlinks(a.String()); err != nil {
|
||||
return err
|
||||
} else {
|
||||
o.lower[i] = check.EscapeOverlayDataSegment(toHost(v))
|
||||
o.lower[i] = toHost(v)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mountOverlay sets up an overlay mount via [ext.FS].
|
||||
func mountOverlay(target string, options [][2]string) error {
|
||||
fs, err := ext.OpenFS(SourceOverlay, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = fs.SetString("source", SourceOverlay); err != nil {
|
||||
_ = fs.Close()
|
||||
return err
|
||||
}
|
||||
for _, option := range options {
|
||||
if err = fs.SetString(option[0], option[1]); err != nil {
|
||||
_ = fs.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = fs.SetFlag(OptionOverlayUserxattr); err != nil {
|
||||
_ = fs.Close()
|
||||
return err
|
||||
}
|
||||
if err = fs.Mount(target, 0); err != nil {
|
||||
_ = fs.Close()
|
||||
return err
|
||||
}
|
||||
return fs.Close()
|
||||
}
|
||||
|
||||
func (o *MountOverlayOp) apply(state *setupState, k syscallDispatcher) error {
|
||||
target := o.Target.String()
|
||||
if !o.noPrefix {
|
||||
@@ -194,7 +221,7 @@ func (o *MountOverlayOp) apply(state *setupState, k syscallDispatcher) error {
|
||||
}
|
||||
}
|
||||
|
||||
options := make([]string, 0, 4)
|
||||
options := make([][2]string, 0, 2+len(o.lower))
|
||||
|
||||
if o.upper == zeroString && o.work == zeroString { // readonly
|
||||
if len(o.Lower) < 2 {
|
||||
@@ -205,15 +232,16 @@ func (o *MountOverlayOp) apply(state *setupState, k syscallDispatcher) error {
|
||||
if len(o.Lower) == 0 {
|
||||
return &OverlayArgumentError{OverlayEmptyLower, zeroString}
|
||||
}
|
||||
options = append(options,
|
||||
OptionOverlayUpperdir+"="+o.upper,
|
||||
OptionOverlayWorkdir+"="+o.work)
|
||||
options = append(options, [][2]string{
|
||||
{OptionOverlayUpperdir, o.upper},
|
||||
{OptionOverlayWorkdir, o.work},
|
||||
}...)
|
||||
}
|
||||
for _, lower := range o.lower {
|
||||
options = append(options, [2]string{OptionOverlayLowerdir + "+", lower})
|
||||
}
|
||||
options = append(options,
|
||||
OptionOverlayLowerdir+"="+strings.Join(o.lower, check.SpecialOverlayPath),
|
||||
OptionOverlayUserxattr)
|
||||
|
||||
return k.mount(SourceOverlay, target, FstypeOverlay, 0, strings.Join(options, check.SpecialOverlayOption))
|
||||
return k.mountOverlay(target, options)
|
||||
}
|
||||
|
||||
func (o *MountOverlayOp) late(*setupState, syscallDispatcher) error { return nil }
|
||||
|
||||
@@ -97,13 +97,12 @@ func TestMountOverlayOp(t *testing.T) {
|
||||
call("mkdirAll", stub.ExpectArgs{"/sysroot", os.FileMode(0705)}, nil, nil),
|
||||
call("mkdirTemp", stub.ExpectArgs{"/", "overlay.upper.*"}, "overlay.upper.32768", nil),
|
||||
call("mkdirTemp", stub.ExpectArgs{"/", "overlay.work.*"}, "overlay.work.32768", nil),
|
||||
call("mount", stub.ExpectArgs{"overlay", "/sysroot", "overlay", uintptr(0), "" +
|
||||
"upperdir=overlay.upper.32768," +
|
||||
"workdir=overlay.work.32768," +
|
||||
"lowerdir=" +
|
||||
`/host/var/lib/planterette/base/debian\:f92c9052:` +
|
||||
`/host/var/lib/planterette/app/org.chromium.Chromium@debian\:f92c9052,` +
|
||||
"userxattr"}, nil, nil),
|
||||
call("mountOverlay", stub.ExpectArgs{"/sysroot", [][2]string{
|
||||
{"upperdir", "overlay.upper.32768"},
|
||||
{"workdir", "overlay.work.32768"},
|
||||
{"lowerdir+", `/host/var/lib/planterette/base/debian:f92c9052`},
|
||||
{"lowerdir+", `/host/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052`},
|
||||
}}, nil, nil),
|
||||
}, nil},
|
||||
|
||||
{"short lower ro", &Params{ParentPerm: 0755}, &MountOverlayOp{
|
||||
@@ -129,11 +128,10 @@ func TestMountOverlayOp(t *testing.T) {
|
||||
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/.ro-store0", nil),
|
||||
}, nil, []stub.Call{
|
||||
call("mkdirAll", stub.ExpectArgs{"/nix/store", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"overlay", "/nix/store", "overlay", uintptr(0), "" +
|
||||
"lowerdir=" +
|
||||
"/host/mnt-root/nix/.ro-store:" +
|
||||
"/host/mnt-root/nix/.ro-store0," +
|
||||
"userxattr"}, nil, nil),
|
||||
call("mountOverlay", stub.ExpectArgs{"/nix/store", [][2]string{
|
||||
{"lowerdir+", "/host/mnt-root/nix/.ro-store"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/.ro-store0"},
|
||||
}}, nil, nil),
|
||||
}, nil},
|
||||
|
||||
{"success ro", &Params{ParentPerm: 0755}, &MountOverlayOp{
|
||||
@@ -147,11 +145,10 @@ func TestMountOverlayOp(t *testing.T) {
|
||||
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store0"}, "/mnt-root/nix/.ro-store0", nil),
|
||||
}, nil, []stub.Call{
|
||||
call("mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0755)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" +
|
||||
"lowerdir=" +
|
||||
"/host/mnt-root/nix/.ro-store:" +
|
||||
"/host/mnt-root/nix/.ro-store0," +
|
||||
"userxattr"}, nil, nil),
|
||||
call("mountOverlay", stub.ExpectArgs{"/sysroot/nix/store", [][2]string{
|
||||
{"lowerdir+", "/host/mnt-root/nix/.ro-store"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/.ro-store0"},
|
||||
}}, nil, nil),
|
||||
}, nil},
|
||||
|
||||
{"nil lower", &Params{ParentPerm: 0700}, &MountOverlayOp{
|
||||
@@ -219,7 +216,11 @@ func TestMountOverlayOp(t *testing.T) {
|
||||
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil),
|
||||
}, nil, []stub.Call{
|
||||
call("mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "upperdir=/host/mnt-root/nix/.rw-store/.upper,workdir=/host/mnt-root/nix/.rw-store/.work,lowerdir=/host/mnt-root/nix/ro-store,userxattr"}, nil, stub.UniqueError(0)),
|
||||
call("mountOverlay", stub.ExpectArgs{"/sysroot/nix/store", [][2]string{
|
||||
{"upperdir", "/host/mnt-root/nix/.rw-store/.upper"},
|
||||
{"workdir", "/host/mnt-root/nix/.rw-store/.work"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store"},
|
||||
}}, nil, stub.UniqueError(0)),
|
||||
}, stub.UniqueError(0)},
|
||||
|
||||
{"success single layer", &Params{ParentPerm: 0700}, &MountOverlayOp{
|
||||
@@ -233,11 +234,11 @@ func TestMountOverlayOp(t *testing.T) {
|
||||
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/ro-store", nil),
|
||||
}, nil, []stub.Call{
|
||||
call("mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" +
|
||||
"upperdir=/host/mnt-root/nix/.rw-store/.upper," +
|
||||
"workdir=/host/mnt-root/nix/.rw-store/.work," +
|
||||
"lowerdir=/host/mnt-root/nix/ro-store," +
|
||||
"userxattr"}, nil, nil),
|
||||
call("mountOverlay", stub.ExpectArgs{"/sysroot/nix/store", [][2]string{
|
||||
{"upperdir", "/host/mnt-root/nix/.rw-store/.upper"},
|
||||
{"workdir", "/host/mnt-root/nix/.rw-store/.work"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store"},
|
||||
}}, nil, nil),
|
||||
}, nil},
|
||||
|
||||
{"success", &Params{ParentPerm: 0700}, &MountOverlayOp{
|
||||
@@ -261,16 +262,15 @@ func TestMountOverlayOp(t *testing.T) {
|
||||
call("evalSymlinks", stub.ExpectArgs{"/mnt-root/nix/.ro-store3"}, "/mnt-root/nix/ro-store3", nil),
|
||||
}, nil, []stub.Call{
|
||||
call("mkdirAll", stub.ExpectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil),
|
||||
call("mount", stub.ExpectArgs{"overlay", "/sysroot/nix/store", "overlay", uintptr(0), "" +
|
||||
"upperdir=/host/mnt-root/nix/.rw-store/.upper," +
|
||||
"workdir=/host/mnt-root/nix/.rw-store/.work," +
|
||||
"lowerdir=" +
|
||||
"/host/mnt-root/nix/ro-store:" +
|
||||
"/host/mnt-root/nix/ro-store0:" +
|
||||
"/host/mnt-root/nix/ro-store1:" +
|
||||
"/host/mnt-root/nix/ro-store2:" +
|
||||
"/host/mnt-root/nix/ro-store3," +
|
||||
"userxattr"}, nil, nil),
|
||||
call("mountOverlay", stub.ExpectArgs{"/sysroot/nix/store", [][2]string{
|
||||
{"upperdir", "/host/mnt-root/nix/.rw-store/.upper"},
|
||||
{"workdir", "/host/mnt-root/nix/.rw-store/.work"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store0"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store1"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store2"},
|
||||
{"lowerdir+", "/host/mnt-root/nix/ro-store3"},
|
||||
}}, nil, nil),
|
||||
}, nil},
|
||||
})
|
||||
|
||||
|
||||
@@ -40,6 +40,9 @@ const (
|
||||
// SourceMqueue is used when mounting mqueue.
|
||||
// Note that any source value is allowed when fstype is [FstypeMqueue].
|
||||
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.
|
||||
// Note that any source value is allowed when fstype is [FstypeOverlay].
|
||||
SourceOverlay = "overlay"
|
||||
@@ -70,6 +73,9 @@ const (
|
||||
// FstypeMqueue represents the mqueue pseudo-filesystem.
|
||||
// This filesystem type is usually mounted on /dev/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.
|
||||
// This filesystem type can be mounted anywhere in the container filesystem.
|
||||
FstypeOverlay = "overlay"
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/vfs"
|
||||
)
|
||||
|
||||
@@ -50,9 +49,6 @@ func TestToHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// InternalToHostOvlEscape exports toHost passed to [check.EscapeOverlayDataSegment].
|
||||
func InternalToHostOvlEscape(s string) string { return check.EscapeOverlayDataSegment(toHost(s)) }
|
||||
|
||||
func TestCreateFile(t *testing.T) {
|
||||
t.Run("nonexistent", func(t *testing.T) {
|
||||
t.Run("mkdir", func(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
package ext
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// include/uapi/linux/mount.h
|
||||
|
||||
/*
|
||||
* move_mount() flags.
|
||||
*/
|
||||
const (
|
||||
MOVE_MOUNT_F_SYMLINKS = 1 << iota /* Follow symlinks on from path */
|
||||
MOVE_MOUNT_F_AUTOMOUNTS /* Follow automounts on from path */
|
||||
MOVE_MOUNT_F_EMPTY_PATH /* Empty from path permitted */
|
||||
_
|
||||
MOVE_MOUNT_T_SYMLINKS /* Follow symlinks on to path */
|
||||
MOVE_MOUNT_T_AUTOMOUNTS /* Follow automounts on to path */
|
||||
MOVE_MOUNT_T_EMPTY_PATH /* Empty to path permitted */
|
||||
_
|
||||
MOVE_MOUNT_SET_GROUP /* Set sharing group instead */
|
||||
MOVE_MOUNT_BENEATH /* Mount beneath top mount */
|
||||
)
|
||||
|
||||
/*
|
||||
* fsopen() flags.
|
||||
*/
|
||||
const (
|
||||
FSOPEN_CLOEXEC = 1 << iota
|
||||
)
|
||||
|
||||
/*
|
||||
* fspick() flags.
|
||||
*/
|
||||
const (
|
||||
FSPICK_CLOEXEC = 1 << iota
|
||||
FSPICK_SYMLINK_NOFOLLOW
|
||||
FSPICK_NO_AUTOMOUNT
|
||||
FSPICK_EMPTY_PATH
|
||||
)
|
||||
|
||||
/*
|
||||
* The type of fsconfig() call made.
|
||||
*/
|
||||
const (
|
||||
FSCONFIG_SET_FLAG = iota /* Set parameter, supplying no value */
|
||||
FSCONFIG_SET_STRING /* Set parameter, supplying a string value */
|
||||
FSCONFIG_SET_BINARY /* Set parameter, supplying a binary blob value */
|
||||
FSCONFIG_SET_PATH /* Set parameter, supplying an object by path */
|
||||
FSCONFIG_SET_PATH_EMPTY /* Set parameter, supplying an object by (empty) path */
|
||||
FSCONFIG_SET_FD /* Set parameter, supplying an object by fd */
|
||||
FSCONFIG_CMD_CREATE /* Create new or reuse existing superblock */
|
||||
FSCONFIG_CMD_RECONFIGURE /* Invoke superblock reconfiguration */
|
||||
FSCONFIG_CMD_CREATE_EXCL /* Create new superblock, fail if reusing existing superblock */
|
||||
)
|
||||
|
||||
/*
|
||||
* fsmount() flags.
|
||||
*/
|
||||
const (
|
||||
FSMOUNT_CLOEXEC = 1 << iota
|
||||
)
|
||||
|
||||
/*
|
||||
* Mount attributes.
|
||||
*/
|
||||
const (
|
||||
MOUNT_ATTR_RDONLY = 0x00000001 /* Mount read-only */
|
||||
MOUNT_ATTR_NOSUID = 0x00000002 /* Ignore suid and sgid bits */
|
||||
MOUNT_ATTR_NODEV = 0x00000004 /* Disallow access to device special files */
|
||||
MOUNT_ATTR_NOEXEC = 0x00000008 /* Disallow program execution */
|
||||
MOUNT_ATTR__ATIME = 0x00000070 /* Setting on how atime should be updated */
|
||||
MOUNT_ATTR_RELATIME = 0x00000000 /* - Update atime relative to mtime/ctime. */
|
||||
MOUNT_ATTR_NOATIME = 0x00000010 /* - Do not update access times. */
|
||||
MOUNT_ATTR_STRICTATIME = 0x00000020 /* - Always perform atime updates */
|
||||
MOUNT_ATTR_NODIRATIME = 0x00000080 /* Do not update directory access times */
|
||||
MOUNT_ATTR_IDMAP = 0x00100000 /* Idmap mount to @userns_fd in struct mount_attr. */
|
||||
MOUNT_ATTR_NOSYMFOLLOW = 0x00200000 /* Do not follow symlinks */
|
||||
)
|
||||
|
||||
// FS provides low-level wrappers around the suite of file-descriptor-based
|
||||
// mount facilities in Linux.
|
||||
type FS struct {
|
||||
fd uintptr
|
||||
c runtime.Cleanup
|
||||
}
|
||||
|
||||
// newFS allocates a new [FS] for the specified fd.
|
||||
func newFS(fd uintptr) *FS {
|
||||
fs := FS{fd: fd}
|
||||
fs.c = runtime.AddCleanup(&fs, func(fd uintptr) {
|
||||
_ = syscall.Close(int(fd))
|
||||
}, fd)
|
||||
return &fs
|
||||
}
|
||||
|
||||
// Close closes the underlying filesystem context.
|
||||
func (fs *FS) Close() error {
|
||||
if fs == nil {
|
||||
return syscall.EINVAL
|
||||
}
|
||||
err := syscall.Close(int(fs.fd))
|
||||
fs.c.Stop()
|
||||
return err
|
||||
}
|
||||
|
||||
// OpenFS creates a new filesystem context.
|
||||
func OpenFS(fsname string, flags int) (fs *FS, err error) {
|
||||
var s *byte
|
||||
s, err = syscall.BytePtrFromString(fsname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fd, _, errno := syscall.Syscall(
|
||||
SYS_FSOPEN,
|
||||
uintptr(unsafe.Pointer(s)),
|
||||
uintptr(flags|FSOPEN_CLOEXEC),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
err = os.NewSyscallError("fsopen", errno)
|
||||
} else {
|
||||
fs = newFS(fd)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// PickFS selects filesystem for reconfiguration.
|
||||
func PickFS(dirfd int, pathname string, flags int) (fs *FS, err error) {
|
||||
var s *byte
|
||||
s, err = syscall.BytePtrFromString(pathname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fd, _, errno := syscall.Syscall(
|
||||
SYS_FSPICK,
|
||||
uintptr(dirfd),
|
||||
uintptr(unsafe.Pointer(s)),
|
||||
uintptr(flags|FSPICK_CLOEXEC),
|
||||
)
|
||||
if errno != 0 {
|
||||
err = os.NewSyscallError("fspick", errno)
|
||||
} else {
|
||||
fs = newFS(fd)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// config configures new or existing filesystem context.
|
||||
func (fs *FS) config(cmd uint, key *byte, value unsafe.Pointer, aux int) (err error) {
|
||||
_, _, errno := syscall.Syscall6(
|
||||
SYS_FSCONFIG,
|
||||
fs.fd,
|
||||
uintptr(cmd),
|
||||
uintptr(unsafe.Pointer(key)),
|
||||
uintptr(value),
|
||||
uintptr(aux),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
err = os.NewSyscallError("fsconfig", errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetFlag sets the flag parameter named by key. ([FSCONFIG_SET_FLAG])
|
||||
func (fs *FS) SetFlag(key string) (err error) {
|
||||
var s *byte
|
||||
s, err = syscall.BytePtrFromString(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return fs.config(FSCONFIG_SET_FLAG, s, nil, 0)
|
||||
}
|
||||
|
||||
// SetString sets the string parameter named by key to the value specified by
|
||||
// value. ([FSCONFIG_SET_STRING])
|
||||
func (fs *FS) SetString(key, value string) (err error) {
|
||||
var s0 *byte
|
||||
s0, err = syscall.BytePtrFromString(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var s1 *byte
|
||||
s1, err = syscall.BytePtrFromString(value)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return fs.config(FSCONFIG_SET_STRING, s0, unsafe.Pointer(s1), 0)
|
||||
}
|
||||
|
||||
// mount instantiates mount object from filesystem context.
|
||||
func (fs *FS) mount(flags, attrFlags int) (fsfd int, err error) {
|
||||
r, _, errno := syscall.Syscall(
|
||||
SYS_FSMOUNT,
|
||||
fs.fd,
|
||||
uintptr(flags|FSMOUNT_CLOEXEC),
|
||||
uintptr(attrFlags),
|
||||
)
|
||||
fsfd = int(r)
|
||||
if errno != 0 {
|
||||
err = os.NewSyscallError("fsmount", errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MoveMount moves or attaches mount object to filesystem.
|
||||
func MoveMount(
|
||||
fromDirfd int,
|
||||
fromPathname string,
|
||||
toDirfd int,
|
||||
toPathname string,
|
||||
flags int,
|
||||
) (err error) {
|
||||
var s0 *byte
|
||||
s0, err = syscall.BytePtrFromString(fromPathname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var s1 *byte
|
||||
s1, err = syscall.BytePtrFromString(toPathname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, errno := syscall.Syscall6(
|
||||
SYS_MOVE_MOUNT,
|
||||
uintptr(fromDirfd),
|
||||
uintptr(unsafe.Pointer(s0)),
|
||||
uintptr(toDirfd),
|
||||
uintptr(unsafe.Pointer(s1)),
|
||||
uintptr(flags),
|
||||
0,
|
||||
)
|
||||
if errno != 0 {
|
||||
err = os.NewSyscallError("move_mount", errno)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Mount attaches the underlying filesystem context to the specified pathname.
|
||||
func (fs *FS) Mount(pathname string, attrFlags int) error {
|
||||
if err := fs.config(FSCONFIG_CMD_CREATE_EXCL, nil, nil, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
fd, err := fs.mount(0, attrFlags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = MoveMount(
|
||||
fd, "",
|
||||
-1, pathname,
|
||||
MOVE_MOUNT_F_EMPTY_PATH,
|
||||
)
|
||||
closeErr := syscall.Close(fd)
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -42,6 +42,8 @@ var (
|
||||
AbsDevShm = unsafeAbs(DevShm)
|
||||
// AbsProc is [Proc] as [check.Absolute].
|
||||
AbsProc = unsafeAbs(Proc)
|
||||
// AbsProcSys is [ProcSys] as [check.Absolute].
|
||||
AbsProcSys = unsafeAbs(ProcSys)
|
||||
// AbsProcSelfExe is [ProcSelfExe] as [check.Absolute].
|
||||
AbsProcSelfExe = unsafeAbs(ProcSelfExe)
|
||||
// AbsSys is [Sys] as [check.Absolute].
|
||||
|
||||
@@ -64,78 +64,6 @@ func TestFlatten(t *testing.T) {
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C"), nil},
|
||||
|
||||
{"sample cache file", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq": {Mode: 0400, Data: []byte{0, 0, 0, 0, 0xad, 0xb, 0, 4, 0xfe, 0xfe, 0, 0, 0xfe, 0xca, 0, 0}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
|
||||
"identifier/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
"identifier/cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
"identifier/deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: 0400, Path: "checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq", Data: []byte{0, 0, 0, 0, 0xad, 0xb, 0, 4, 0xfe, 0xfe, 0, 0, 0xfe, 0xca, 0, 0}},
|
||||
{Mode: 0400, Path: "checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX", Data: []byte{0}},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq", Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe", Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX", Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("St9rlE-mGZ5gXwiv_hzQ_B8bZP-UUvSNmf4nHUZzCMOumb6hKnheZSe0dmnuc4Q2"), nil},
|
||||
|
||||
{"sample http get cure", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU": {Mode: 0400, Data: []byte("\x7f\xe1\x69\xa2\xdd\x63\x96\x26\x83\x79\x61\x8b\xf0\x3f\xd5\x16\x9a\x39\x3a\xdb\xcf\xb1\xbc\x8d\x33\xff\x75\xee\x62\x56\xa9\xf0\x27\xac\x13\x94\x69")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/oM-2pUlk-mOxK1t3aMWZer69UdOQlAXiAgMrpZ1476VoOqpYVP1aGFS9_HYy-D8_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: 0400, Path: "checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU", Data: []byte("\x7f\xe1\x69\xa2\xdd\x63\x96\x26\x83\x79\x61\x8b\xf0\x3f\xd5\x16\x9a\x39\x3a\xdb\xcf\xb1\xbc\x8d\x33\xff\x75\xee\x62\x56\xa9\xf0\x27\xac\x13\x94\x69")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/oM-2pUlk-mOxK1t3aMWZer69UdOQlAXiAgMrpZ1476VoOqpYVP1aGFS9_HYy-D8_", Data: []byte("../checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("L_0RFHpr9JUS4Zp14rz2dESSRvfLzpvqsLhR1-YjQt8hYlmEdVl7vI3_-v8UNPKs"), nil},
|
||||
|
||||
{"sample directory step simple", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
|
||||
"lib": {Mode: fs.ModeDir | 0700},
|
||||
"lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"lib/pkgconfig": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0500, Path: "."},
|
||||
|
||||
{Mode: 0400, Path: "check", Data: []byte{0, 0}},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "lib"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "lib/libedac.so", Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "lib/pkgconfig"},
|
||||
}, pkg.MustDecode("qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b"), nil},
|
||||
|
||||
{"sample directory step garbage", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
@@ -151,421 +79,6 @@ func TestFlatten(t *testing.T) {
|
||||
|
||||
{Mode: fs.ModeDir | 0500, Path: "lib/pkgconfig"},
|
||||
}, pkg.MustDecode("CUx-3hSbTWPsbMfDhgalG4Ni_GmR9TnVX8F99tY_P5GtkYvczg9RrF5zO0jX9XYT"), nil},
|
||||
|
||||
{"sample directory", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib/pkgconfig": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
"identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b"},
|
||||
{Mode: 0400, Path: "checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/check", Data: []byte{0, 0}},
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib/libedac.so", Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib/pkgconfig"},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY", Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa", Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("WVpvsVqVKg9Nsh744x57h51AuWUoUR2nnh8Md-EYBQpk6ziyTuUn6PLtF2e0Eu_d"), nil},
|
||||
|
||||
{"sample no assume checksum", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M/check": {Mode: 0400, Data: []byte{}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/_wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")},
|
||||
"identifier/_wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M"},
|
||||
{Mode: 0400, Path: "checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M/check", Data: []byte{}},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/_wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/_wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("OC290t23aimNo2Rp2pPwan5GI2KRLRdOwYxXQMD9jw0QROgHnNXWodoWdV0hwu2w"), nil},
|
||||
|
||||
{"sample tar step unpack", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/pkgconfig": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0500},
|
||||
"identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
"identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0500},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0500, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP"},
|
||||
{Mode: 0400, Path: "checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/check", Data: []byte{0, 0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/libedac.so", Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/pkgconfig"},
|
||||
|
||||
{Mode: fs.ModeDir | 0500, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY", Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa", Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
|
||||
{Mode: fs.ModeDir | 0500, Path: "work"},
|
||||
}, pkg.MustDecode("cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM"), nil},
|
||||
|
||||
{"sample tar", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/pkgconfig": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/identifier": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
"checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/work": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/W5S65DEhawz_WKaok5NjUKLmnD9dNl5RPauNJjcOVcB3VM4eGhSaLGmXbL8vZpiw": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM")},
|
||||
"identifier/rg7F1D5hwv6o4xctjD5zDq4i5MD0mArTsUIWfhUbik8xC6Bsyt3mjXXOm3goojTz": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP"},
|
||||
{Mode: 0400, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/check", Data: []byte{0, 0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/libedac.so", Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/pkgconfig"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY", Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa", Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM/work"},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/W5S65DEhawz_WKaok5NjUKLmnD9dNl5RPauNJjcOVcB3VM4eGhSaLGmXbL8vZpiw", Data: []byte("../checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/rg7F1D5hwv6o4xctjD5zDq4i5MD0mArTsUIWfhUbik8xC6Bsyt3mjXXOm3goojTz", Data: []byte("../checksum/cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("NQTlc466JmSVLIyWklm_u8_g95jEEb98PxJU-kjwxLpfdjwMWJq0G8ze9R4Vo1Vu"), nil},
|
||||
|
||||
{"sample tar expand step unpack", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0500, Path: "."},
|
||||
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "libedac.so", Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
}, pkg.MustDecode("CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN"), nil},
|
||||
|
||||
{"sample tar expand", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/W5S65DEhawz_WKaok5NjUKLmnD9dNl5RPauNJjcOVcB3VM4eGhSaLGmXbL8vZpiw": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN")},
|
||||
"identifier/_v1blm2h-_KA-dVaawdpLas6MjHc6rbhhFS8JWwx8iJxZGUu8EBbRrhr5AaZ9PJL": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN/libedac.so", Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/W5S65DEhawz_WKaok5NjUKLmnD9dNl5RPauNJjcOVcB3VM4eGhSaLGmXbL8vZpiw", Data: []byte("../checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/_v1blm2h-_KA-dVaawdpLas6MjHc6rbhhFS8JWwx8iJxZGUu8EBbRrhr5AaZ9PJL", Data: []byte("../checksum/CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("hSoSSgCYTNonX3Q8FjvjD1fBl-E-BQyA6OTXro2OadXqbST4tZ-akGXszdeqphRe"), nil},
|
||||
|
||||
{"testtool", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"check": {Mode: 0400, Data: []byte{0}},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0500, Path: "."},
|
||||
|
||||
{Mode: 0400, Path: "check", Data: []byte{0}},
|
||||
}, pkg.MustDecode("GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"), nil},
|
||||
|
||||
{"sample exec container", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
"identifier/dztPS6jRjiZtCF4_p8AzfnxGp6obkhrgFVsxdodbKWUoAEVtDz3MykepJB4kI_ks": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"},
|
||||
{Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"},
|
||||
{Mode: 0400, Path: "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb", Data: []byte{}},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/dztPS6jRjiZtCF4_p8AzfnxGp6obkhrgFVsxdodbKWUoAEVtDz3MykepJB4kI_ks", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("Q5DluWQCAeohLoiGRImurwFp3vdz9IfQCoj7Fuhh73s4KQPRHpEQEnHTdNHmB8Fx"), nil},
|
||||
|
||||
{"testtool net", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"check": {Mode: 0400, Data: []byte("net")},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0500, Path: "."},
|
||||
|
||||
{Mode: 0400, Path: "check", Data: []byte("net")},
|
||||
}, pkg.MustDecode("a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W"), nil},
|
||||
|
||||
{"sample exec net container", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
|
||||
"checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W/check": {Mode: 0400, Data: []byte("net")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/G8qPxD9puvvoOVV7lrT80eyDeIl3G_CCFoKw12c8mCjMdG1zF7NEPkwYpNubClK3": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")},
|
||||
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"},
|
||||
{Mode: 0400, Path: "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb", Data: []byte{}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W"},
|
||||
{Mode: 0400, Path: "checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W/check", Data: []byte("net")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/G8qPxD9puvvoOVV7lrT80eyDeIl3G_CCFoKw12c8mCjMdG1zF7NEPkwYpNubClK3", Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("bPYvvqxpfV7xcC1EptqyKNK1klLJgYHMDkzBcoOyK6j_Aj5hb0mXNPwTwPSK5F6Z"), nil},
|
||||
|
||||
{"sample exec container overlay root", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/RdMA-mubnrHuu3Ky1wWyxauSYCO0ZH_zCPUj3uDHqkfwv5sGcByoF_g5PjlGiClb": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"},
|
||||
{Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/RdMA-mubnrHuu3Ky1wWyxauSYCO0ZH_zCPUj3uDHqkfwv5sGcByoF_g5PjlGiClb", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("PO2DSSCa4yoSgEYRcCSZfQfwow1yRigL3Ry-hI0RDI4aGuFBha-EfXeSJnG_5_Rl"), nil},
|
||||
|
||||
{"sample exec container overlay work", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/5hlaukCirnXE4W_RSLJFOZN47Z5RiHnacXzdFp_70cLgiJUGR6cSb_HaFftkzi0-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"},
|
||||
{Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/5hlaukCirnXE4W_RSLJFOZN47Z5RiHnacXzdFp_70cLgiJUGR6cSb_HaFftkzi0-", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("iaRt6l_Wm2n-h5UsDewZxQkCmjZjyL8r7wv32QT2kyV55-Lx09Dq4gfg9BiwPnKs"), nil},
|
||||
|
||||
{"sample exec container multiple layers", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
|
||||
"checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK/check": {Mode: 0400, Data: []byte("layers")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
"identifier/B-kc5iJMx8GtlCua4dz6BiJHnDAOUfPjgpbKq4e-QEn0_CZkSYs3fOA1ve06qMs2": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK")},
|
||||
"identifier/p1t_drXr34i-jZNuxDMLaMOdL6tZvQqhavNafGynGqxOZoXAUTSn7kqNh3Ovv3DT": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"},
|
||||
{Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"},
|
||||
{Mode: 0400, Path: "checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb", Data: []byte{}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK"},
|
||||
{Mode: 0400, Path: "checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK/check", Data: []byte("layers")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/B-kc5iJMx8GtlCua4dz6BiJHnDAOUfPjgpbKq4e-QEn0_CZkSYs3fOA1ve06qMs2", Data: []byte("../checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/p1t_drXr34i-jZNuxDMLaMOdL6tZvQqhavNafGynGqxOZoXAUTSn7kqNh3Ovv3DT", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("O2YzyR7IUGU5J2CADy0hUZ3A5NkP_Vwzs4UadEdn2oMZZVWRtH0xZGJ3HXiimTnZ"), nil},
|
||||
|
||||
{"sample exec container layer promotion", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/kvJIqZo5DKFOxC2ZQ-8_nPaQzEAz9cIm3p6guO-uLqm-xaiPu7oRkSnsu411jd_U": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
"identifier/xXTIYcXmgJWNLC91c417RRrNM9cjELwEZHpGvf8Fk_GNP5agRJp_SicD0w9aMeLJ": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9"},
|
||||
{Mode: 0400, Path: "checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9/check", Data: []byte{0}},
|
||||
{Mode: fs.ModeDir | 0500, Path: "checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/kvJIqZo5DKFOxC2ZQ-8_nPaQzEAz9cIm3p6guO-uLqm-xaiPu7oRkSnsu411jd_U", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK", Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/xXTIYcXmgJWNLC91c417RRrNM9cjELwEZHpGvf8Fk_GNP5agRJp_SicD0w9aMeLJ", Data: []byte("../checksum/GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "temp"},
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("3EaW6WibLi9gl03_UieiFPaFcPy5p4x3JPxrnLJxGaTI-bh3HU9DK9IMx7c3rrNm"), nil},
|
||||
|
||||
{"sample file short", fstest.MapFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX": {Mode: 0400, Data: []byte{0}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/3376ALA7hIUm2LbzH2fDvRezgzod1eTK_G6XjyOgbM2u-6swvkFaF0BOwSl_juBi": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, []pkg.FlatEntry{
|
||||
{Mode: fs.ModeDir | 0700, Path: "."},
|
||||
{Mode: fs.ModeDir | 0700, Path: "checksum"},
|
||||
{Mode: 0400, Path: "checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX", Data: []byte{0}},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "identifier"},
|
||||
{Mode: fs.ModeSymlink | 0777, Path: "identifier/3376ALA7hIUm2LbzH2fDvRezgzod1eTK_G6XjyOgbM2u-6swvkFaF0BOwSl_juBi", Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
|
||||
|
||||
{Mode: fs.ModeDir | 0700, Path: "work"},
|
||||
}, pkg.MustDecode("iR6H5OIsyOW4EwEgtm9rGzGF6DVtyHLySEtwnFE8bnus9VJcoCbR4JIek7Lw-vwT"), nil},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
+105
-35
@@ -9,8 +9,10 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unique"
|
||||
@@ -94,6 +96,32 @@ func MustPath(pathname string, writable bool, a ...Artifact) ExecPath {
|
||||
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 (
|
||||
// ExecTimeoutDefault replaces out of range [NewExec] timeout values.
|
||||
ExecTimeoutDefault = 15 * time.Minute
|
||||
@@ -110,6 +138,8 @@ type execArtifact struct {
|
||||
// Caller-supplied user-facing reporting name, guaranteed to be nonzero
|
||||
// during initialisation.
|
||||
name string
|
||||
// Target architecture.
|
||||
arch string
|
||||
// Caller-supplied inner mount points.
|
||||
paths []ExecPath
|
||||
|
||||
@@ -132,28 +162,40 @@ type execArtifact struct {
|
||||
|
||||
var _ fmt.Stringer = new(execArtifact)
|
||||
|
||||
// execNetArtifact is like execArtifact but implements [KnownChecksum] and has
|
||||
// its resulting container keep the host net namespace.
|
||||
type execNetArtifact struct {
|
||||
// execMeasuredArtifact is like execArtifact but implements [KnownChecksum] and
|
||||
// has its resulting container optionally keep the host net namespace.
|
||||
type execMeasuredArtifact struct {
|
||||
checksum Checksum
|
||||
|
||||
// Whether to keep host net namespace.
|
||||
hostNet bool
|
||||
|
||||
execArtifact
|
||||
}
|
||||
|
||||
var _ KnownChecksum = new(execNetArtifact)
|
||||
var _ KnownChecksum = new(execMeasuredArtifact)
|
||||
|
||||
// Checksum returns the caller-supplied checksum.
|
||||
func (a *execNetArtifact) Checksum() Checksum { return a.checksum }
|
||||
func (a *execMeasuredArtifact) Checksum() Checksum { return a.checksum }
|
||||
|
||||
// Kind returns the hardcoded [Kind] constant.
|
||||
func (*execNetArtifact) Kind() Kind { return KindExecNet }
|
||||
// Kind returns [KindExecNet], or [KindExec] if hostNet is false.
|
||||
func (a *execMeasuredArtifact) Kind() Kind {
|
||||
if a == nil || a.hostNet {
|
||||
return KindExecNet
|
||||
}
|
||||
return KindExec
|
||||
}
|
||||
|
||||
// Cure cures the [Artifact] in the container described by the caller. The
|
||||
// container retains host networking.
|
||||
func (a *execNetArtifact) Cure(f *FContext) error {
|
||||
return a.cure(f, true)
|
||||
// container optionally retains host networking.
|
||||
func (a *execMeasuredArtifact) Cure(f *FContext) error {
|
||||
return a.cure(f, a.hostNet)
|
||||
}
|
||||
|
||||
// ErrNetChecksum is panicked by [NewExec] if host net namespace is requested
|
||||
// with a nil checksum.
|
||||
var ErrNetChecksum = errors.New("attempting to keep net namespace without checksum")
|
||||
|
||||
// NewExec returns a new [Artifact] that executes the program path in a
|
||||
// container with specified paths bind mounted read-only in order. A private
|
||||
// instance of /proc and /dev is made available to the container.
|
||||
@@ -167,7 +209,7 @@ func (a *execNetArtifact) Cure(f *FContext) error {
|
||||
// regular or symlink.
|
||||
//
|
||||
// If checksum is non-nil, the resulting [Artifact] implements [KnownChecksum]
|
||||
// and its container runs in the host net namespace.
|
||||
// and its container optionally runs in the host net namespace.
|
||||
//
|
||||
// The container is allowed to run for the specified duration before the initial
|
||||
// process and all processes originating from it is terminated. A zero or
|
||||
@@ -178,10 +220,10 @@ func (a *execNetArtifact) Cure(f *FContext) error {
|
||||
// container and does not affect curing outcome. Because of this, it is omitted
|
||||
// from parameter data for computing identifier.
|
||||
func NewExec(
|
||||
name string,
|
||||
name, arch string,
|
||||
checksum *Checksum,
|
||||
timeout time.Duration,
|
||||
exclusive bool,
|
||||
hostNet, exclusive bool,
|
||||
|
||||
dir *check.Absolute,
|
||||
env []string,
|
||||
@@ -193,17 +235,23 @@ func NewExec(
|
||||
if name == "" {
|
||||
name = "exec-" + filepath.Base(pathname.String())
|
||||
}
|
||||
if arch == "" {
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
if timeout <= 0 {
|
||||
timeout = ExecTimeoutDefault
|
||||
}
|
||||
if 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 hostNet {
|
||||
panic(ErrNetChecksum)
|
||||
}
|
||||
return &a
|
||||
}
|
||||
return &execNetArtifact{*checksum, a}
|
||||
return &execMeasuredArtifact{*checksum, hostNet, a}
|
||||
}
|
||||
|
||||
// Kind returns the hardcoded [Kind] constant.
|
||||
@@ -211,6 +259,7 @@ func (*execArtifact) Kind() Kind { return KindExec }
|
||||
|
||||
// Params writes paths, executable pathname and args.
|
||||
func (a *execArtifact) Params(ctx *IContext) {
|
||||
ctx.WriteString(a.arch)
|
||||
ctx.WriteString(a.name)
|
||||
|
||||
ctx.WriteUint32(uint32(len(a.paths)))
|
||||
@@ -257,11 +306,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
|
||||
// or execNetArtifact.
|
||||
func readExecArtifact(r *IRReader, net bool) Artifact {
|
||||
r.DiscardAll()
|
||||
|
||||
arch := r.ReadString()
|
||||
if arch == "" {
|
||||
panic(UnsupportedArchError(arch))
|
||||
}
|
||||
|
||||
name := r.ReadString()
|
||||
|
||||
sz := r.ReadUint32()
|
||||
@@ -312,22 +376,17 @@ func readExecArtifact(r *IRReader, net bool) Artifact {
|
||||
exclusive := r.ReadUint32() != 0
|
||||
|
||||
checksum, ok := r.Finalise()
|
||||
|
||||
var checksumP *Checksum
|
||||
if net {
|
||||
if !ok {
|
||||
panic(ErrExpectedChecksum)
|
||||
}
|
||||
checksumVal := checksum.Value()
|
||||
checksumP = &checksumVal
|
||||
} else {
|
||||
if ok {
|
||||
panic(ErrUnexpectedChecksum)
|
||||
}
|
||||
if ok {
|
||||
checksumP = new(checksum.Value())
|
||||
}
|
||||
|
||||
if net && !ok {
|
||||
panic(ErrExpectedChecksum)
|
||||
}
|
||||
|
||||
return NewExec(
|
||||
name, checksumP, timeout, exclusive, dir, env, pathname, args, paths...,
|
||||
name, arch, checksumP, timeout, net, exclusive, dir, env, pathname, args, paths...,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -436,11 +495,23 @@ func (a *execArtifact) makeContainer(
|
||||
if z.HostNet {
|
||||
z.Hostname = "cure-net"
|
||||
}
|
||||
z.Quiet = flags&CSuppressInit != 0
|
||||
z.Uid, z.Gid = (1<<10)-1, (1<<10)-1
|
||||
z.Dir, z.Path, z.Args = a.dir, a.path, a.args
|
||||
z.Env = slices.Concat(a.env, []string{EnvJobs + "=" + strconv.Itoa(jobs)})
|
||||
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 {
|
||||
if i == overlayWorkIndex {
|
||||
if err = os.MkdirAll(work.String(), 0700); err != nil {
|
||||
@@ -527,9 +598,9 @@ func (c *Cache) EnterExec(
|
||||
case *execArtifact:
|
||||
e = f
|
||||
|
||||
case *execNetArtifact:
|
||||
case *execMeasuredArtifact:
|
||||
e = &f.execArtifact
|
||||
hostNet = true
|
||||
hostNet = f.hostNet
|
||||
|
||||
default:
|
||||
return ErrNotExec
|
||||
@@ -630,12 +701,6 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
|
||||
_ = stdout.Close()
|
||||
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)
|
||||
stdoutDone, stderrDone := make(chan struct{}), make(chan struct{})
|
||||
@@ -650,6 +715,11 @@ func (a *execArtifact) cure(f *FContext, hostNet bool) (err error) {
|
||||
io.TeeReader(brStderr, status),
|
||||
)
|
||||
defer func() {
|
||||
if err != nil && !errors.As(err, new(*exec.ExitError)) {
|
||||
_ = stdout.Close()
|
||||
_ = stderr.Close()
|
||||
}
|
||||
|
||||
<-stdoutDone
|
||||
<-stderrDone
|
||||
f.cache.putReader(brStdout)
|
||||
|
||||
+297
-50
@@ -1,44 +1,70 @@
|
||||
package pkg_test
|
||||
|
||||
//go:generate env CGO_ENABLED=0 go build -tags testtool -o testdata/testtool ./testdata
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"encoding/gob"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
"unique"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/container"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/info"
|
||||
"hakurei.app/internal/pkg"
|
||||
"hakurei.app/internal/stub"
|
||||
|
||||
"hakurei.app/internal/pkg/internal/testtool/expected"
|
||||
)
|
||||
|
||||
// testtoolBin is the container test tool binary made available to the
|
||||
// execArtifact for testing its curing environment.
|
||||
//
|
||||
//go:embed testdata/testtool
|
||||
//go:generate env CGO_ENABLED=0 go build -tags testtool -o internal/testtool ./internal/testtool
|
||||
//go:embed internal/testtool/testtool
|
||||
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) {
|
||||
t.Parallel()
|
||||
|
||||
wantChecksumOffline := pkg.MustDecode(
|
||||
"GPa4aBakdSJd7Tz7LYj_VJFoojzyZinmVcG3k6M5xI6CZ821J5sXLhLDDuS47gi9",
|
||||
)
|
||||
wantOffline := expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"check": {Mode: 0400, Data: []byte{0}},
|
||||
}
|
||||
wantOfflineEncode := pkg.Encode(wantOffline.hash())
|
||||
failingArtifact := &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
params: []byte("doomed artifact"),
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return stub.UniqueError(0xcafe)
|
||||
},
|
||||
}
|
||||
|
||||
checkWithCache(t, []cacheTestCase{
|
||||
{"offline", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
{"offline", pkg.CValidateKnown | checkDestroySubstitutes, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
testtool, testtoolDestroy := newTesttool()
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-offline", nil, 0, false,
|
||||
"exec-offline", "", new(wantOffline.hash()), 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -58,67 +84,128 @@ func TestExec(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
pkg.MustPath("/opt", false, testtool),
|
||||
), ignorePathname, wantChecksumOffline, nil},
|
||||
), ignorePathname, wantOffline, nil},
|
||||
|
||||
{"error passthrough", pkg.NewExec(
|
||||
"", nil, 0, true,
|
||||
{"substitution", pkg.NewExec(
|
||||
"exec-offline", "", new(wantOffline.hash()), 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
[]string{"testtool"},
|
||||
|
||||
pkg.MustPath("/proc/nonexistent", false, &stubArtifact{
|
||||
pkg.MustPath("/file", false, newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xfe, 0},
|
||||
nil,
|
||||
nil, nil,
|
||||
)),
|
||||
// substitution miss fails in testtool due to differing idents
|
||||
pkg.MustPath("/.hakurei", false, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
params: []byte("doomed artifact"),
|
||||
params: []byte("empty directory (substituted)"),
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return stub.UniqueError(0xcafe)
|
||||
return os.MkdirAll(t.GetWorkDir().String(), 0700)
|
||||
},
|
||||
}),
|
||||
), nil, pkg.Checksum{}, &pkg.DependencyCureError{
|
||||
pkg.MustPath("/opt", false, testtool),
|
||||
), ignorePathname, wantOffline, nil},
|
||||
|
||||
{"error passthrough", pkg.NewExec(
|
||||
"", "", nil, 0, false, true,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
[]string{"testtool"},
|
||||
|
||||
pkg.MustPath("/proc/nonexistent", false, failingArtifact),
|
||||
), nil, nil, &pkg.DependencyCureError{
|
||||
{
|
||||
Ident: unique.Make(pkg.ID(pkg.MustDecode(
|
||||
"Sowo6oZRmG6xVtUaxB6bDWZhVsqAJsIJWUp0OPKlE103cY0lodx7dem8J-qQF0Z1",
|
||||
))),
|
||||
A: failingArtifact,
|
||||
Err: stub.UniqueError(0xcafe),
|
||||
},
|
||||
}},
|
||||
|
||||
{"invalid paths", pkg.NewExec(
|
||||
"", nil, 0, false,
|
||||
"", "", nil, 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
[]string{"testtool"},
|
||||
|
||||
pkg.ExecPath{},
|
||||
), nil, pkg.Checksum{}, pkg.ErrInvalidPaths},
|
||||
), nil, nil, pkg.ErrInvalidPaths},
|
||||
})
|
||||
|
||||
// check init failure passthrough
|
||||
var exitError *exec.ExitError
|
||||
if _, _, err := c.Cure(pkg.NewExec(
|
||||
"", nil, 0, false,
|
||||
initFailureArtifact := pkg.NewExec(
|
||||
"", "", nil, 0, false, false,
|
||||
pkg.AbsWork,
|
||||
nil,
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
[]string{"testtool"},
|
||||
)); !errors.As(err, &exitError) ||
|
||||
)
|
||||
var exitError *exec.ExitError
|
||||
if _, _, err := c.Cure(initFailureArtifact); !errors.As(err, &exitError) ||
|
||||
exitError.ExitCode() != hst.ExitFailure {
|
||||
t.Fatalf("Cure: error = %v, want init exit status 1", err)
|
||||
}
|
||||
|
||||
testtoolDestroy(t, base, c)
|
||||
}, pkg.MustDecode("Q5DluWQCAeohLoiGRImurwFp3vdz9IfQCoj7Fuhh73s4KQPRHpEQEnHTdNHmB8Fx")},
|
||||
var faultStatus []byte
|
||||
if faults, err := c.ReadFaults(initFailureArtifact); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(faults) != 1 {
|
||||
t.Fatalf("ReadFaults: %v", faults)
|
||||
} else if faultStatus, err = os.ReadFile(faults[0].String()); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err = faults[0].Destroy(); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
t.Logf("destroyed expected fault at %s", faults[0].Time().UTC())
|
||||
}
|
||||
|
||||
{"net", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
if !bytes.HasPrefix(faultStatus, []byte(
|
||||
"internal/pkg ",
|
||||
)) || !bytes.Contains(faultStatus, []byte(
|
||||
"\ninit: fork/exec /opt/bin/testtool: no such file or directory\n",
|
||||
)) {
|
||||
t.Errorf("unexpected status:\n%s", string(faultStatus))
|
||||
}
|
||||
|
||||
destroyStatus(t, base, 2, 1)
|
||||
testtoolDestroy(t, base, c)
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantOfflineEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantOfflineEncode + "/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/IY91PCtOpCYy21AaIK0c9f8-Z6fb2_2ewoHWkt4dxoLf0GOrWqS8yAGFLV84b1Dw": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
|
||||
"identifier/QwS7SmiatdqryQYgESdGw7Yw2PcpNf0vNfpvUA0t92BTlKiUjfCrXyMW17G2X77X": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
"identifier/" + expected.Offline: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"net", pkg.CValidateKnown | checkDestroySubstitutes, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
testtool, testtoolDestroy := newTesttool()
|
||||
|
||||
wantChecksum := pkg.MustDecode(
|
||||
"a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W",
|
||||
)
|
||||
wantNet := expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"check": {Mode: 0400, Data: []byte("net")},
|
||||
}
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-net", &wantChecksum, 0, false,
|
||||
"exec-net", "", new(wantNet.hash()), 0, true, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -138,18 +225,37 @@ func TestExec(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
pkg.MustPath("/opt", false, testtool),
|
||||
), ignorePathname, wantChecksum, nil},
|
||||
), ignorePathname, wantNet, nil},
|
||||
})
|
||||
|
||||
destroyStatus(t, base, 2, 0)
|
||||
testtoolDestroy(t, base, c)
|
||||
}, pkg.MustDecode("bPYvvqxpfV7xcC1EptqyKNK1klLJgYHMDkzBcoOyK6j_Aj5hb0mXNPwTwPSK5F6Z")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
{"overlay root", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
|
||||
"checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W/check": {Mode: 0400, Data: []byte("net")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/" + expected.Net: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/a1F_i9PVQI4qMcoHgTQkORuyWLkC1GLIxOhDt2JpU1NGAxWc5VJzdlfRK-PYBh3W")},
|
||||
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"overlay root", pkg.CValidateKnown | checkDestroySubstitutes, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
testtool, testtoolDestroy := newTesttool()
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-overlay-root", nil, 0, false,
|
||||
"exec-overlay-root", "", nil, 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -163,18 +269,35 @@ func TestExec(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
pkg.MustPath("/opt", false, testtool),
|
||||
), ignorePathname, wantChecksumOffline, nil},
|
||||
), ignorePathname, wantOffline, nil},
|
||||
})
|
||||
|
||||
destroyStatus(t, base, 2, 0)
|
||||
testtoolDestroy(t, base, c)
|
||||
}, pkg.MustDecode("PO2DSSCa4yoSgEYRcCSZfQfwow1yRigL3Ry-hI0RDI4aGuFBha-EfXeSJnG_5_Rl")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
{"overlay work", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantOfflineEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantOfflineEncode + "/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/" + expected.OvlRoot: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"overlay work", pkg.CValidateKnown | checkDestroySubstitutes, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
testtool, testtoolDestroy := newTesttool()
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-overlay-work", nil, 0, false,
|
||||
"exec-overlay-work", "", nil, 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/work/bin/testtool"),
|
||||
@@ -193,18 +316,35 @@ func TestExec(t *testing.T) {
|
||||
return os.MkdirAll(t.GetWorkDir().String(), 0700)
|
||||
},
|
||||
}), pkg.Path(pkg.AbsWork, false /* ignored */, testtool),
|
||||
), ignorePathname, wantChecksumOffline, nil},
|
||||
), ignorePathname, wantOffline, nil},
|
||||
})
|
||||
|
||||
destroyStatus(t, base, 2, 0)
|
||||
testtoolDestroy(t, base, c)
|
||||
}, pkg.MustDecode("iaRt6l_Wm2n-h5UsDewZxQkCmjZjyL8r7wv32QT2kyV55-Lx09Dq4gfg9BiwPnKs")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
{"multiple layers", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantOfflineEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantOfflineEncode + "/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/" + expected.Work: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"multiple layers", pkg.CValidateKnown | checkDestroySubstitutes, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
testtool, testtoolDestroy := newTesttool()
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-multiple-layers", nil, 0, false,
|
||||
"exec-multiple-layers", "", nil, 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -245,18 +385,40 @@ func TestExec(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
pkg.MustPath("/opt", false, testtool),
|
||||
), ignorePathname, wantChecksumOffline, nil},
|
||||
), ignorePathname, wantOffline, nil},
|
||||
})
|
||||
|
||||
destroyStatus(t, base, 2, 0)
|
||||
testtoolDestroy(t, base, c)
|
||||
}, pkg.MustDecode("O2YzyR7IUGU5J2CADy0hUZ3A5NkP_Vwzs4UadEdn2oMZZVWRtH0xZGJ3HXiimTnZ")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
{"overlay layer promotion", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantOfflineEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantOfflineEncode + "/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb": {Mode: 0400, Data: []byte{}},
|
||||
"checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK/check": {Mode: 0400, Data: []byte("layers")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/_gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/OLBgp1GsljhM2TJ-sbHjaiH9txEUvgdDTAzHv2P24donTt6_529l-9Ua0vFImLlb")},
|
||||
"identifier/B-kc5iJMx8GtlCua4dz6BiJHnDAOUfPjgpbKq4e-QEn0_CZkSYs3fOA1ve06qMs2": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK")},
|
||||
"identifier/" + expected.Layers: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"overlay layer promotion", pkg.CValidateKnown | checkDestroySubstitutes, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
testtool, testtoolDestroy := newTesttool()
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"container", pkg.NewExec(
|
||||
"exec-layer-promotion", nil, 0, true,
|
||||
"exec-layer-promotion", "", nil, 0, false, true,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1", "HAKUREI_ROOT=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -276,11 +438,96 @@ func TestExec(t *testing.T) {
|
||||
},
|
||||
}),
|
||||
pkg.MustPath("/opt", false, testtool),
|
||||
), ignorePathname, wantChecksumOffline, nil},
|
||||
), ignorePathname, wantOffline, nil},
|
||||
})
|
||||
|
||||
destroyStatus(t, base, 2, 0)
|
||||
testtoolDestroy(t, base, c)
|
||||
}, pkg.MustDecode("3EaW6WibLi9gl03_UieiFPaFcPy5p4x3JPxrnLJxGaTI-bh3HU9DK9IMx7c3rrNm")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantOfflineEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantOfflineEncode + "/check": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/kvJIqZo5DKFOxC2ZQ-8_nPaQzEAz9cIm3p6guO-uLqm-xaiPu7oRkSnsu411jd_U": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
"identifier/vjz1MHPcGBKV7sjcs8jQP3cqxJ1hgPTiQBMCEHP9BGXjGxd-tJmEmXKaStObo5gK": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU")},
|
||||
"identifier/" + expected.Promote: {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantOfflineEncode)},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"binfmt", pkg.CValidateKnown | checkDestroySubstitutes, 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, false, 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},
|
||||
})
|
||||
|
||||
destroyStatus(t, base, 2, 0)
|
||||
}, 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")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package pkg_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"testing"
|
||||
|
||||
"hakurei.app/check"
|
||||
@@ -10,18 +11,27 @@ import (
|
||||
func TestFile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
want := expectsFile{0}
|
||||
checkWithCache(t, []cacheTestCase{
|
||||
{"file", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
cureMany(t, c, []cureStep{
|
||||
{"short", pkg.NewFile("null", []byte{0}), base.Append(
|
||||
"identifier",
|
||||
"3376ALA7hIUm2LbzH2fDvRezgzod1eTK_G6XjyOgbM2u-6swvkFaF0BOwSl_juBi",
|
||||
), pkg.MustDecode(
|
||||
"vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX",
|
||||
), nil},
|
||||
), want, nil},
|
||||
})
|
||||
}, pkg.MustDecode(
|
||||
"iR6H5OIsyOW4EwEgtm9rGzGF6DVtyHLySEtwnFE8bnus9VJcoCbR4JIek7Lw-vwT",
|
||||
)},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + pkg.Encode(want.hash()): {Mode: 0400, Data: []byte{0}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/3376ALA7hIUm2LbzH2fDvRezgzod1eTK_G6XjyOgbM2u-6swvkFaF0BOwSl_juBi": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package expected
|
||||
|
||||
const (
|
||||
Offline = "q5ktDTq0miP-VvB2blxqXQeaRXCUWgP_KbC18KNtUDtyoaI_h5mHmGuPMArVEBDs"
|
||||
OvlRoot = "NacZGXwuRkTvcHaG08a22ujJ8qCWN0RSoFlRSR5FSt0ZcBbJ28FRvkYsHEtX7G8i"
|
||||
Layers = "WBJDrATtX6rIE5yAu8ePX3WmDF0Tt9kFiue0m3cRnyRoVx1my8a67fh3CAW486oP"
|
||||
Net = "CmYtj2sNB3LHtqiDuck_Lz3MjLLIiwyP8N4NDitQ1Icvv__LVP9p8tm-sHeQaKKp"
|
||||
Promote = "TX3eCloaQFkV-SZIH6Jg6E5WKH--rcXY1P0jnZKmLFKWrNqnOzd4G9eIBh6i5ywN"
|
||||
Work = "OuNiLSC68pZhAOr1YQ4WbV1tzASA0nxLEBcK7lO7MqxDY_j8dmP_C612RTuF23Lu"
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package expected
|
||||
|
||||
const (
|
||||
Offline = "WapqyoPxbWSnq07dWHt71mHaJXq99pAjJfFlELlJljSiZMhTFqqlzU1_mN86shSj"
|
||||
OvlRoot = "V9anFOiRvjGfAeBhLl14AL8TKdWZyD0WTPYe4fS9mOBw8iW5Lmarvt6TG6MV8uWm"
|
||||
Layers = "tKx7JNRoSBdK_7MdzI-nwTNV2wmiPzwYdcd17oLmXKL_iLmUzUiA79qTqdrTasrv"
|
||||
Net = "aXyDLzBCJ9XltXZIfetEVsEkrqHfcXuD5XE_FcUnYbN3emwL55N6P8LlHzNfGnM5"
|
||||
Promote = "3k4V16n96Lq04gjFSKmm4sFjyQ883FFBNXgTy9s_DjeTwxT3pg_iacEh8yMb_S4m"
|
||||
Work = "6Q49MhFWRE3Ne6MycwAotgl1GtoU5WCHqJNWG2byYZCY-zX-IxPrWiKk7bKkNzhE"
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package expected
|
||||
|
||||
const (
|
||||
Offline = "Z6yXE5gOJScL3srmnVMWgCXccDiUNZ5snSrf6RkXuU1_U0rX_kGVwsfHUgNG_awd"
|
||||
OvlRoot = "zYXJHFRLuxvUhuisZEXgGgVvdQd6piMfp5jmtT6jdVjvC2gICXquOq-UTwlrSD5I"
|
||||
Layers = "_F8EDazHbcLeT0sVSQXRN_kn9IjduqJcDYgzXpsT-hpKU4EBcZ0PISN2zchpqMbm"
|
||||
Net = "CA_FAaSIYJgapBEHV40doxpH23PdUEy_6s1TZc7wfSPN0XYqwGpMceXXDSabGveO"
|
||||
Promote = "_3LPrLp--4h9k4GsNNApu9hHtAafq-GUhfU6d4hJKBDKT3bz_szOsvkXxc5sK53d"
|
||||
Work = "FEgHeiCD_WT4wsfB-9kDH5n6cRWCEYtJmXdKZgmUUukAOoXumH_hLlosXREC-tqq"
|
||||
)
|
||||
@@ -14,15 +14,29 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/fhs"
|
||||
"hakurei.app/vfs"
|
||||
|
||||
"hakurei.app/internal/pkg/internal/testtool/expected"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(0)
|
||||
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 {
|
||||
return s == "CURE_JOBS="+strconv.Itoa(runtime.NumCPU())
|
||||
})
|
||||
@@ -148,59 +162,40 @@ func main() {
|
||||
}
|
||||
|
||||
const checksumEmptyDir = "MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU"
|
||||
ident := "dztPS6jRjiZtCF4_p8AzfnxGp6obkhrgFVsxdodbKWUoAEVtDz3MykepJB4kI_ks"
|
||||
ident := expected.Offline
|
||||
log.Println(m)
|
||||
next := func() { m = m.Next; log.Println(m) }
|
||||
|
||||
if overlayRoot {
|
||||
ident = "RdMA-mubnrHuu3Ky1wWyxauSYCO0ZH_zCPUj3uDHqkfwv5sGcByoF_g5PjlGiClb"
|
||||
ident = expected.OvlRoot
|
||||
|
||||
if m.Root != "/" || m.Target != "/" ||
|
||||
m.Source != "overlay" || m.FsType != "overlay" {
|
||||
log.Fatal("unexpected root mount entry")
|
||||
}
|
||||
var lowerdir string
|
||||
var lowerdir []string
|
||||
for _, o := range strings.Split(m.FsOptstr, ",") {
|
||||
const lowerdirKey = "lowerdir="
|
||||
const lowerdirKey = "lowerdir+="
|
||||
if strings.HasPrefix(o, lowerdirKey) {
|
||||
lowerdir = o[len(lowerdirKey):]
|
||||
lowerdir = append(lowerdir, o[len(lowerdirKey):])
|
||||
}
|
||||
}
|
||||
if !layers {
|
||||
if filepath.Base(lowerdir) != checksumEmptyDir {
|
||||
if len(lowerdir) != 1 || filepath.Base(lowerdir[0]) != checksumEmptyDir {
|
||||
log.Fatal("unexpected artifact checksum")
|
||||
}
|
||||
} else {
|
||||
ident = "p1t_drXr34i-jZNuxDMLaMOdL6tZvQqhavNafGynGqxOZoXAUTSn7kqNh3Ovv3DT"
|
||||
ident = expected.Layers
|
||||
|
||||
lowerdirsEscaped := strings.Split(lowerdir, ":")
|
||||
lowerdirs := lowerdirsEscaped[:0]
|
||||
// ignore the option separator since it does not appear in ident
|
||||
for i, e := range lowerdirsEscaped {
|
||||
if len(e) > 0 &&
|
||||
e[len(e)-1] == check.SpecialOverlayEscape[0] &&
|
||||
(len(e) == 1 || e[len(e)-2] != check.SpecialOverlayEscape[0]) {
|
||||
// ignore escaped pathname separator since it does not
|
||||
// appear in ident
|
||||
|
||||
e = e[:len(e)-1]
|
||||
if len(lowerdirsEscaped) != i {
|
||||
lowerdirsEscaped[i+1] = e + lowerdirsEscaped[i+1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
lowerdirs = append(lowerdirs, e)
|
||||
}
|
||||
|
||||
if len(lowerdirs) != 2 ||
|
||||
filepath.Base(lowerdirs[0]) != "MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU" ||
|
||||
filepath.Base(lowerdirs[1]) != "nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK" {
|
||||
log.Fatalf("unexpected lowerdirs %s", strings.Join(lowerdirs, ", "))
|
||||
if len(lowerdir) != 2 ||
|
||||
filepath.Base(lowerdir[0]) != "MGWmEfjut2QE2xPJwTsmUzpff4BN_FEnQ7T0j7gvUCCiugJQNwqt9m151fm9D1yU" ||
|
||||
filepath.Base(lowerdir[1]) != "nY_CUdiaUM1OL4cPr5TS92FCJ3rCRV7Hm5oVTzAvMXwC03_QnTRfQ5PPs7mOU9fK" {
|
||||
log.Fatalf("unexpected lowerdirs %s", strings.Join(lowerdir, ", "))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if hostNet {
|
||||
ident = "G8qPxD9puvvoOVV7lrT80eyDeIl3G_CCFoKw12c8mCjMdG1zF7NEPkwYpNubClK3"
|
||||
ident = expected.Net
|
||||
}
|
||||
|
||||
if m.Root != "/sysroot" || m.Target != "/" {
|
||||
@@ -219,14 +214,14 @@ func main() {
|
||||
}
|
||||
|
||||
if promote {
|
||||
ident = "xXTIYcXmgJWNLC91c417RRrNM9cjELwEZHpGvf8Fk_GNP5agRJp_SicD0w9aMeLJ"
|
||||
ident = expected.Promote
|
||||
}
|
||||
|
||||
next() // testtool artifact
|
||||
|
||||
next()
|
||||
if overlayWork {
|
||||
ident = "5hlaukCirnXE4W_RSLJFOZN47Z5RiHnacXzdFp_70cLgiJUGR6cSb_HaFftkzi0-"
|
||||
ident = expected.Work
|
||||
if m.Root != "/" || m.Target != "/work" ||
|
||||
m.Source != "overlay" || m.FsType != "overlay" {
|
||||
log.Fatal("unexpected work mount entry")
|
||||
+47
-11
@@ -76,6 +76,9 @@ type IContext struct {
|
||||
// Written to by various methods, should be zeroed after [Artifact.Params]
|
||||
// returns and must not be exposed directly.
|
||||
w io.Writer
|
||||
// Optional [Artifact] to cureRes cache, replaces [IRKindIdent] with
|
||||
// checksum values if non-nil.
|
||||
inputs map[Artifact]cureRes
|
||||
}
|
||||
|
||||
// irZero is a zero IR word.
|
||||
@@ -163,7 +166,15 @@ func (i *IContext) WriteIdent(a Artifact) {
|
||||
defer i.ic.putIdentBuf(buf)
|
||||
|
||||
IRKindIdent.encodeHeader(0).put(buf[:])
|
||||
*(*ID)(buf[wordSize:]) = i.ic.Ident(a).Value()
|
||||
if i.inputs != nil {
|
||||
res, ok := i.inputs[a]
|
||||
if !ok {
|
||||
panic(InvalidLookupError(i.ic.Ident(a).Value()))
|
||||
}
|
||||
*(*ID)(buf[wordSize:]) = res.checksum.Value()
|
||||
} else {
|
||||
*(*ID)(buf[wordSize:]) = i.ic.Ident(a).Value()
|
||||
}
|
||||
i.mustWrite(buf[:])
|
||||
}
|
||||
|
||||
@@ -207,19 +218,44 @@ func (i *IContext) WriteString(s string) {
|
||||
// Encode writes a deterministic, efficient representation of a to w and returns
|
||||
// the first non-nil error encountered while writing to w.
|
||||
func (ic *irCache) Encode(w io.Writer, a Artifact) (err error) {
|
||||
return ic.encode(w, a, nil)
|
||||
}
|
||||
|
||||
// encode implements Encode but replaces identifiers with their cured checksums
|
||||
// for a non-nil ident. Caller must acquire Cache.identMu.
|
||||
func (ic *irCache) encode(
|
||||
w io.Writer,
|
||||
a Artifact,
|
||||
inputs map[Artifact]cureRes,
|
||||
) (err error) {
|
||||
deps := a.Dependencies()
|
||||
idents := make([]*extIdent, len(deps))
|
||||
for i, d := range deps {
|
||||
dbuf, did := ic.unsafeIdent(d, true)
|
||||
if dbuf == nil {
|
||||
dbuf = ic.getIdentBuf()
|
||||
if inputs == nil {
|
||||
for i, d := range deps {
|
||||
dbuf, did := ic.unsafeIdent(d, true)
|
||||
if dbuf == nil {
|
||||
dbuf = ic.getIdentBuf()
|
||||
binary.LittleEndian.PutUint64(dbuf[:], uint64(d.Kind()))
|
||||
*(*ID)(dbuf[wordSize:]) = did.Value()
|
||||
} else {
|
||||
ic.storeIdent(d, dbuf)
|
||||
}
|
||||
defer ic.putIdentBuf(dbuf)
|
||||
idents[i] = dbuf
|
||||
}
|
||||
} else {
|
||||
for i, d := range deps {
|
||||
res, ok := inputs[d]
|
||||
if !ok {
|
||||
return InvalidLookupError(ic.Ident(d).Value())
|
||||
}
|
||||
|
||||
dbuf := ic.getIdentBuf()
|
||||
binary.LittleEndian.PutUint64(dbuf[:], uint64(d.Kind()))
|
||||
*(*ID)(dbuf[wordSize:]) = did.Value()
|
||||
} else {
|
||||
ic.storeIdent(d, dbuf)
|
||||
*(*ID)(dbuf[wordSize:]) = res.checksum.Value()
|
||||
defer ic.putIdentBuf(dbuf)
|
||||
idents[i] = dbuf
|
||||
}
|
||||
defer ic.putIdentBuf(dbuf)
|
||||
idents[i] = dbuf
|
||||
}
|
||||
slices.SortFunc(idents, func(a, b *extIdent) int {
|
||||
return bytes.Compare(a[:], b[:])
|
||||
@@ -244,7 +280,7 @@ func (ic *irCache) Encode(w io.Writer, a Artifact) (err error) {
|
||||
}
|
||||
|
||||
func() {
|
||||
i := IContext{ic, w}
|
||||
i := IContext{ic, w, inputs}
|
||||
|
||||
defer panicToError(&err)
|
||||
defer func() { i.ic, i.w = nil, nil }()
|
||||
|
||||
+33
-6
@@ -3,6 +3,7 @@ package pkg_test
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/fs"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -38,7 +39,7 @@ func TestIRRoundtrip(t *testing.T) {
|
||||
)},
|
||||
|
||||
{"exec offline", pkg.NewExec(
|
||||
"exec-offline", nil, 0, false,
|
||||
"exec-offline", "", nil, 0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -58,9 +59,9 @@ func TestIRRoundtrip(t *testing.T) {
|
||||
)},
|
||||
|
||||
{"exec net", pkg.NewExec(
|
||||
"exec-net",
|
||||
"exec-net", "",
|
||||
(*pkg.Checksum)(bytes.Repeat([]byte{0xfc}, len(pkg.Checksum{}))),
|
||||
0, false,
|
||||
0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
@@ -79,6 +80,28 @@ func TestIRRoundtrip(t *testing.T) {
|
||||
)),
|
||||
)},
|
||||
|
||||
{"exec measured", pkg.NewExec(
|
||||
"exec-measured", "",
|
||||
(*pkg.Checksum)(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
|
||||
0, false, false,
|
||||
pkg.AbsWork,
|
||||
[]string{"HAKUREI_TEST=1"},
|
||||
check.MustAbs("/opt/bin/testtool"),
|
||||
[]string{"testtool", "measured"},
|
||||
|
||||
pkg.MustPath("/file", false, pkg.NewFile("file", []byte(
|
||||
"stub file",
|
||||
))), pkg.MustPath("/.hakurei", false, pkg.NewHTTPGetTar(
|
||||
nil, "file:///hakurei.tar",
|
||||
pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
|
||||
pkg.TarUncompressed,
|
||||
)), pkg.MustPath("/opt", false, pkg.NewHTTPGetTar(
|
||||
nil, "file:///testtool.tar.gz",
|
||||
pkg.Checksum(bytes.Repeat([]byte{0xfd}, len(pkg.Checksum{}))),
|
||||
pkg.TarGzip,
|
||||
)),
|
||||
)},
|
||||
|
||||
{"file anonymous", pkg.NewFile("", []byte{0})},
|
||||
{"file", pkg.NewFile("stub", []byte("stub"))},
|
||||
}
|
||||
@@ -105,9 +128,13 @@ func TestIRRoundtrip(t *testing.T) {
|
||||
if err := <-done; err != nil {
|
||||
t.Fatalf("EncodeAll: error = %v", err)
|
||||
}
|
||||
}, pkg.MustDecode(
|
||||
"E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C",
|
||||
),
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
},
|
||||
}
|
||||
}
|
||||
checkWithCache(t, testCasesCache)
|
||||
|
||||
@@ -3,6 +3,7 @@ package pkg_test
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
@@ -85,7 +86,13 @@ func TestHTTPGet(t *testing.T) {
|
||||
if _, err := f.Cure(r); !reflect.DeepEqual(err, wantErrNotFound) {
|
||||
t.Fatalf("Cure: error = %#v, want %#v", err, wantErrNotFound)
|
||||
}
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"cure", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
r := newRContext(t, c)
|
||||
@@ -144,6 +151,18 @@ func TestHTTPGet(t *testing.T) {
|
||||
if _, _, err := c.Cure(f); !reflect.DeepEqual(err, wantErrNotFound) {
|
||||
t.Fatalf("Pathname: error = %#v, want %#v", err, wantErrNotFound)
|
||||
}
|
||||
}, pkg.MustDecode("L_0RFHpr9JUS4Zp14rz2dESSRvfLzpvqsLhR1-YjQt8hYlmEdVl7vI3_-v8UNPKs")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU": {Mode: 0400, Data: []byte("\x7f\xe1\x69\xa2\xdd\x63\x96\x26\x83\x79\x61\x8b\xf0\x3f\xd5\x16\x9a\x39\x3a\xdb\xcf\xb1\xbc\x8d\x33\xff\x75\xee\x62\x56\xa9\xf0\x27\xac\x13\x94\x69")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/oM-2pUlk-mOxK1t3aMWZer69UdOQlAXiAgMrpZ1476VoOqpYVP1aGFS9_HYy-D8_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/fLYGIMHgN1louE-JzITJZJo2SDniPu-IHBXubtvQWFO-hXnDVKNuscV7-zlyr5fU")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
+316
-77
@@ -4,6 +4,7 @@ package pkg
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"cmp"
|
||||
"context"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"io/fs"
|
||||
"maps"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
@@ -24,6 +26,7 @@ import (
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
"unique"
|
||||
"unsafe"
|
||||
|
||||
@@ -247,7 +250,14 @@ func (t *TContext) destroy(errP *error) {
|
||||
*errP = errors.Join(*errP, err)
|
||||
}
|
||||
if *errP != nil {
|
||||
*errP = errors.Join(*errP, os.Remove(t.statusPath.String()))
|
||||
*errP = errors.Join(*errP, os.Rename(
|
||||
t.statusPath.String(), t.cache.base.Append(
|
||||
dirFault,
|
||||
t.ids+"."+strconv.FormatUint(uint64(
|
||||
time.Now().UnixNano(),
|
||||
), 10),
|
||||
).String(),
|
||||
))
|
||||
}
|
||||
t.status = nil
|
||||
}
|
||||
@@ -508,33 +518,34 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
// fileLock is the file name appended to Cache.base for guaranteeing
|
||||
// exclusive access to the cache directory.
|
||||
// fileLock is the lock file for exclusive access to the cache directory.
|
||||
fileLock = "lock"
|
||||
// fileVariant is the file name appended to Cache.base holding the variant
|
||||
// identification string set by a prior call to [SetExtension].
|
||||
// fileVariant is a file holding the variant identification string set by a
|
||||
// prior call to [SetExtension].
|
||||
fileVariant = "variant"
|
||||
|
||||
// dirIdentifier is the directory name appended to Cache.base for storing
|
||||
// artifacts named after their [ID].
|
||||
// dirSubstitute holds symlinks to artifacts by checksum, named after their
|
||||
// substitute identifier.
|
||||
dirSubstitute = "substitute"
|
||||
// dirIdentifier holds symlinks to artifacts by checksum, named after their
|
||||
// IR-based identifier.
|
||||
dirIdentifier = "identifier"
|
||||
// dirChecksum is the directory name appended to Cache.base for storing
|
||||
// artifacts named after their [Checksum].
|
||||
// dirChecksum holds artifacts named after their [Checksum].
|
||||
dirChecksum = "checksum"
|
||||
// dirStatus is the directory name appended to Cache.base for storing
|
||||
// artifact metadata and logs named after their [ID].
|
||||
// dirStatus holds artifact metadata and logs named after their IR-based
|
||||
// identifier. For [FloodArtifact], the same file is also available under
|
||||
// its substitute identifier.
|
||||
dirStatus = "status"
|
||||
// dirFault holds status files of faulted cures.
|
||||
dirFault = "fault"
|
||||
|
||||
// dirWork is the directory name appended to Cache.base for working
|
||||
// pathnames set up during [Cache.Cure].
|
||||
// dirWork holds working pathnames set up during [Cache.Cure].
|
||||
dirWork = "work"
|
||||
// dirTemp is the directory name appended to Cache.base for scratch space
|
||||
// pathnames allocated during [Cache.Cure].
|
||||
// dirTemp holds scratch space allocated during [Cache.Cure].
|
||||
dirTemp = "temp"
|
||||
|
||||
// dirExecScratch is the directory name appended to Cache.base for scratch
|
||||
// space setting up the container started by [Cache.EnterExec]. Exclusivity
|
||||
// via Cache.inExec.
|
||||
// dirExecScratch is scratch space set up for the container started by
|
||||
// [Cache.EnterExec]. Exclusivity via Cache.inExec.
|
||||
dirExecScratch = "scratch"
|
||||
|
||||
// checksumLinknamePrefix is prepended to the encoded [Checksum] value
|
||||
@@ -617,6 +628,13 @@ const (
|
||||
// CPromoteVariant allows [pkg.Open] to promote an unextended on-disk cache
|
||||
// to the current extension variant. This is a one-way operation.
|
||||
CPromoteVariant
|
||||
|
||||
// CSuppressInit arranges for verbose output of the container init to be
|
||||
// suppressed regardless of [message.Msg] state.
|
||||
CSuppressInit
|
||||
|
||||
// CIgnoreSubstitutes disables content-based dependency substitution.
|
||||
CIgnoreSubstitutes
|
||||
)
|
||||
|
||||
// toplevel holds [context.WithCancel] over caller-supplied context, where all
|
||||
@@ -672,6 +690,11 @@ type Cache struct {
|
||||
// Synchronises access to dirChecksum.
|
||||
checksumMu sync.RWMutex
|
||||
|
||||
// Presence of an alternative in the cache. Keys are not valid identifiers
|
||||
// and must not be used as such.
|
||||
substitute map[unique.Handle[ID]]unique.Handle[Checksum]
|
||||
// Synchronises access to substitute and corresponding filesystem entries.
|
||||
substituteMu sync.RWMutex
|
||||
// Identifier to content pair cache.
|
||||
ident map[unique.Handle[ID]]unique.Handle[Checksum]
|
||||
// Identifier to error pair for unrecoverably faulted [Artifact].
|
||||
@@ -882,11 +905,14 @@ func (c *Cache) Scrub(checks int) error {
|
||||
checks = runtime.NumCPU()
|
||||
}
|
||||
|
||||
c.substituteMu.Lock()
|
||||
defer c.substituteMu.Unlock()
|
||||
c.identMu.Lock()
|
||||
defer c.identMu.Unlock()
|
||||
c.checksumMu.Lock()
|
||||
defer c.checksumMu.Unlock()
|
||||
|
||||
c.substitute = make(map[unique.Handle[ID]]unique.Handle[Checksum])
|
||||
c.ident = make(map[unique.Handle[ID]]unique.Handle[Checksum])
|
||||
c.identErr = make(map[unique.Handle[ID]]error)
|
||||
c.artifact.Clear()
|
||||
@@ -994,47 +1020,52 @@ func (c *Cache) Scrub(checks int) error {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
dir = c.base.Append(dirIdentifier)
|
||||
if entries, readdirErr := os.ReadDir(dir.String()); readdirErr != nil {
|
||||
addErr(dir, readdirErr)
|
||||
} else {
|
||||
wg.Add(len(entries))
|
||||
for _, ent := range entries {
|
||||
w <- checkEntry{ent, func(ent os.DirEntry, want *Checksum) bool {
|
||||
got := p.Get().(*Checksum)
|
||||
defer p.Put(got)
|
||||
for _, suffix := range []string{
|
||||
dirSubstitute,
|
||||
dirIdentifier,
|
||||
} {
|
||||
dir = c.base.Append(suffix)
|
||||
if entries, readdirErr := os.ReadDir(dir.String()); readdirErr != nil {
|
||||
addErr(dir, readdirErr)
|
||||
} else {
|
||||
wg.Add(len(entries))
|
||||
for _, ent := range entries {
|
||||
w <- checkEntry{ent, func(ent os.DirEntry, want *Checksum) bool {
|
||||
got := p.Get().(*Checksum)
|
||||
defer p.Put(got)
|
||||
|
||||
pathname := dir.Append(ent.Name())
|
||||
if linkname, err := os.Readlink(
|
||||
pathname.String(),
|
||||
); err != nil {
|
||||
seMu.Lock()
|
||||
se.Errs[pathname.Handle()] = append(se.Errs[pathname.Handle()], err)
|
||||
se.DanglingIdentifiers = append(se.DanglingIdentifiers, *want)
|
||||
seMu.Unlock()
|
||||
return false
|
||||
} else if err = Decode(got, filepath.Base(linkname)); err != nil {
|
||||
seMu.Lock()
|
||||
lnp := dir.Append(linkname)
|
||||
se.Errs[lnp.Handle()] = append(se.Errs[lnp.Handle()], err)
|
||||
se.DanglingIdentifiers = append(se.DanglingIdentifiers, *want)
|
||||
seMu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err := os.Stat(pathname.String()); err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
addErr(pathname, err)
|
||||
pathname := dir.Append(ent.Name())
|
||||
if linkname, err := os.Readlink(
|
||||
pathname.String(),
|
||||
); err != nil {
|
||||
seMu.Lock()
|
||||
se.Errs[pathname.Handle()] = append(se.Errs[pathname.Handle()], err)
|
||||
se.DanglingIdentifiers = append(se.DanglingIdentifiers, *want)
|
||||
seMu.Unlock()
|
||||
return false
|
||||
} else if err = Decode(got, filepath.Base(linkname)); err != nil {
|
||||
seMu.Lock()
|
||||
lnp := dir.Append(linkname)
|
||||
se.Errs[lnp.Handle()] = append(se.Errs[lnp.Handle()], err)
|
||||
se.DanglingIdentifiers = append(se.DanglingIdentifiers, *want)
|
||||
seMu.Unlock()
|
||||
return false
|
||||
}
|
||||
seMu.Lock()
|
||||
se.DanglingIdentifiers = append(se.DanglingIdentifiers, *want)
|
||||
seMu.Unlock()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}}
|
||||
|
||||
if _, err := os.Stat(pathname.String()); err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
addErr(pathname, err)
|
||||
}
|
||||
seMu.Lock()
|
||||
se.DanglingIdentifiers = append(se.DanglingIdentifiers, *want)
|
||||
seMu.Unlock()
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}}
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
dir = c.base.Append(dirStatus)
|
||||
@@ -1182,6 +1213,52 @@ func (c *Cache) finaliseIdent(
|
||||
close(done)
|
||||
}
|
||||
|
||||
// zeroChecksum is a zero [Checksum] handle, used for comparison only.
|
||||
var zeroChecksum unique.Handle[Checksum]
|
||||
|
||||
// loadSubstitute returns a checksum corresponding to a substitute identifier,
|
||||
// or zeroChecksum if an alternative is not available.
|
||||
func (c *Cache) loadSubstitute(
|
||||
substitute unique.Handle[ID],
|
||||
) (unique.Handle[Checksum], error) {
|
||||
c.substituteMu.RLock()
|
||||
if checksum, ok := c.substitute[substitute]; ok {
|
||||
c.substituteMu.RUnlock()
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
linkname, err := os.Readlink(c.base.Append(
|
||||
dirSubstitute,
|
||||
Encode(substitute.Value()),
|
||||
).String())
|
||||
c.substituteMu.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return zeroChecksum, err
|
||||
}
|
||||
|
||||
c.substituteMu.Lock()
|
||||
c.substitute[substitute] = zeroChecksum
|
||||
c.substituteMu.Unlock()
|
||||
return zeroChecksum, nil
|
||||
}
|
||||
|
||||
var checksum unique.Handle[Checksum]
|
||||
buf := c.getIdentBuf()
|
||||
err = Decode((*Checksum)(buf[:]), filepath.Base(linkname))
|
||||
if err == nil {
|
||||
checksum = unique.Make(Checksum(buf[:]))
|
||||
|
||||
c.substituteMu.Lock()
|
||||
c.substitute[substitute] = checksum
|
||||
c.substituteMu.Unlock()
|
||||
}
|
||||
c.putIdentBuf(buf)
|
||||
|
||||
return checksum, err
|
||||
}
|
||||
|
||||
// Done returns a channel that is closed when the ongoing cure of an [Artifact]
|
||||
// referred to by the specified identifier completes. Done may return nil if
|
||||
// no ongoing cure of the specified identifier exists.
|
||||
@@ -1416,8 +1493,8 @@ func (c *Cache) Cure(a Artifact) (
|
||||
|
||||
// CureError wraps a non-nil error returned attempting to cure an [Artifact].
|
||||
type CureError struct {
|
||||
Ident unique.Handle[ID]
|
||||
Err error
|
||||
A Artifact
|
||||
Err error
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error.
|
||||
@@ -1430,40 +1507,63 @@ func (e *CureError) Error() string { return e.Err.Error() }
|
||||
type DependencyCureError []*CureError
|
||||
|
||||
// unwrapM recursively expands underlying errors into a caller-supplied map.
|
||||
func (e *DependencyCureError) unwrapM(me map[unique.Handle[ID]]*CureError) {
|
||||
func (e *DependencyCureError) unwrapM(
|
||||
ctx context.Context,
|
||||
ir *IRCache,
|
||||
me map[unique.Handle[ID]]*CureError,
|
||||
) {
|
||||
for _, err := range *e {
|
||||
if _, ok := me[err.Ident]; ok {
|
||||
if ctx.Err() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
id := ir.Ident(err.A)
|
||||
if _, ok := me[id]; ok {
|
||||
continue
|
||||
}
|
||||
if _e, ok := err.Err.(*DependencyCureError); ok {
|
||||
_e.unwrapM(me)
|
||||
_e.unwrapM(ctx, ir, me)
|
||||
continue
|
||||
}
|
||||
me[err.Ident] = err
|
||||
me[id] = err
|
||||
}
|
||||
}
|
||||
|
||||
// unwrap recursively expands and deduplicates underlying errors.
|
||||
func (e *DependencyCureError) unwrap() DependencyCureError {
|
||||
func (e *DependencyCureError) unwrap(
|
||||
ctx context.Context,
|
||||
ir *IRCache,
|
||||
) DependencyCureError {
|
||||
me := make(map[unique.Handle[ID]]*CureError)
|
||||
e.unwrapM(me)
|
||||
errs := slices.AppendSeq(
|
||||
make(DependencyCureError, 0, len(me)),
|
||||
maps.Values(me),
|
||||
)
|
||||
e.unwrapM(ctx, ir, me)
|
||||
type ent struct {
|
||||
id unique.Handle[ID]
|
||||
err *CureError
|
||||
}
|
||||
errs := make([]*ent, 0, len(me))
|
||||
for id, err := range me {
|
||||
errs = append(errs, &ent{id, err})
|
||||
}
|
||||
|
||||
var identBuf [2]ID
|
||||
slices.SortFunc(errs, func(a, b *CureError) int {
|
||||
identBuf[0], identBuf[1] = a.Ident.Value(), b.Ident.Value()
|
||||
slices.SortFunc(errs, func(a, b *ent) int {
|
||||
identBuf[0], identBuf[1] = a.id.Value(), b.id.Value()
|
||||
return slices.Compare(identBuf[0][:], identBuf[1][:])
|
||||
})
|
||||
|
||||
return errs
|
||||
_errs := make(DependencyCureError, len(errs))
|
||||
for i, v := range errs {
|
||||
_errs[i] = v.err
|
||||
}
|
||||
return _errs
|
||||
}
|
||||
|
||||
// Unwrap returns a deduplicated slice of underlying errors.
|
||||
func (e *DependencyCureError) Unwrap() []error {
|
||||
errs := e.unwrap()
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
|
||||
errs := e.unwrap(ctx, NewIR())
|
||||
_errs := make([]error, len(errs))
|
||||
for i, err := range errs {
|
||||
_errs[i] = err
|
||||
@@ -1473,14 +1573,23 @@ func (e *DependencyCureError) Unwrap() []error {
|
||||
|
||||
// Error returns a user-facing multiline error message.
|
||||
func (e *DependencyCureError) Error() string {
|
||||
errs := e.unwrap()
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer cancel()
|
||||
|
||||
ir := NewIR()
|
||||
errs := e.unwrap(ctx, ir)
|
||||
if len(errs) == 0 {
|
||||
return "invalid dependency cure outcome"
|
||||
}
|
||||
var buf strings.Builder
|
||||
buf.WriteString("errors curing dependencies:")
|
||||
for _, err := range errs {
|
||||
buf.WriteString("\n\t" + Encode(err.Ident.Value()) + ": " + err.Error())
|
||||
buf.WriteString("\n\t" +
|
||||
reportName(err.A, ir.Ident(err.A)) + ": " +
|
||||
err.Error())
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
buf.WriteString("\nerror resolution cancelled")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
@@ -1650,16 +1759,44 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
return
|
||||
}
|
||||
|
||||
var checksums string
|
||||
var (
|
||||
checksums string
|
||||
substitute unique.Handle[ID]
|
||||
alternative *check.Absolute
|
||||
)
|
||||
defer func() {
|
||||
if err == nil && checksums != "" {
|
||||
linkname := checksumLinknamePrefix + checksums
|
||||
|
||||
err = os.Symlink(
|
||||
checksumLinknamePrefix+checksums,
|
||||
linkname,
|
||||
pathname.String(),
|
||||
)
|
||||
if err == nil {
|
||||
err = zeroTimes(pathname.String())
|
||||
}
|
||||
|
||||
if err == nil && alternative != nil {
|
||||
c.substituteMu.Lock()
|
||||
err = os.Symlink(
|
||||
linkname,
|
||||
alternative.String(),
|
||||
)
|
||||
if errors.Is(err, os.ErrExist) {
|
||||
c.msg.Verbosef(
|
||||
"creating alternative over %s for artifact %s",
|
||||
Encode(substitute.Value()), ids,
|
||||
)
|
||||
err = nil
|
||||
}
|
||||
if err == nil {
|
||||
err = zeroTimes(alternative.String())
|
||||
}
|
||||
if err == nil && checksum != zeroChecksum {
|
||||
c.substitute[substitute] = checksum
|
||||
}
|
||||
c.substituteMu.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -1856,11 +1993,64 @@ func (c *Cache) cure(a Artifact, curesExempt bool) (
|
||||
f.deps[deps[i]] = p
|
||||
}
|
||||
|
||||
sh := sha512.New384()
|
||||
err = c.encode(sh, a, f.deps)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := c.getIdentBuf()
|
||||
sh.Sum(buf[wordSize:wordSize])
|
||||
substitute = unique.Make(ID(buf[wordSize:]))
|
||||
substitutes := Encode(substitute.Value())
|
||||
c.putIdentBuf(buf)
|
||||
alternative = c.base.Append(
|
||||
dirSubstitute,
|
||||
substitutes,
|
||||
)
|
||||
|
||||
if c.flags&CIgnoreSubstitutes == 0 {
|
||||
var substituteChecksum unique.Handle[Checksum]
|
||||
substituteChecksum, err = c.loadSubstitute(substitute)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if substituteChecksum != zeroChecksum {
|
||||
checksum = substituteChecksum
|
||||
checksums = Encode(checksum.Value())
|
||||
checksumPathname = c.base.Append(
|
||||
dirChecksum,
|
||||
checksums,
|
||||
)
|
||||
if _, err = os.Lstat(c.base.Append(
|
||||
dirStatus,
|
||||
substitutes,
|
||||
).String()); err == nil {
|
||||
err = os.Symlink(substitutes, c.base.Append(
|
||||
dirStatus,
|
||||
ids,
|
||||
).String())
|
||||
} else if errors.Is(err, os.ErrNotExist) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
defer f.destroy(&err)
|
||||
if err = c.enterCure(a, curesExempt); err != nil {
|
||||
return
|
||||
}
|
||||
err = ca.Cure(&f)
|
||||
if err == nil && f.status != nil {
|
||||
err = os.Link(c.base.Append(
|
||||
dirStatus,
|
||||
ids,
|
||||
).String(), c.base.Append(
|
||||
dirStatus,
|
||||
substitutes,
|
||||
).String())
|
||||
}
|
||||
c.exitCure(a, curesExempt)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -1953,7 +2143,7 @@ func (pending *pendingArtifactDep) cure(c *Cache) {
|
||||
}
|
||||
|
||||
pending.errsMu.Lock()
|
||||
*pending.errs = append(*pending.errs, &CureError{c.Ident(pending.a), err})
|
||||
*pending.errs = append(*pending.errs, &CureError{pending.a, err})
|
||||
pending.errsMu.Unlock()
|
||||
}
|
||||
|
||||
@@ -1969,6 +2159,52 @@ func (c *Cache) OpenStatus(a Artifact) (r io.ReadSeekCloser, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Fault holds the pathname and termination time of an [Artifact] fault entry.
|
||||
type Fault struct {
|
||||
*check.Absolute
|
||||
t uint64
|
||||
}
|
||||
|
||||
// Time returns the instant in time where the fault occurred.
|
||||
func (f Fault) Time() time.Time { return time.Unix(0, int64(f.t)) }
|
||||
|
||||
// Open opens the underlying entry for reading.
|
||||
func (f Fault) Open() (io.ReadCloser, error) { return os.Open(f.Absolute.String()) }
|
||||
|
||||
// Destroy removes the underlying fault entry.
|
||||
func (f Fault) Destroy() error { return os.Remove(f.Absolute.String()) }
|
||||
|
||||
// ReadFaults returns fault entries for an [Artifact].
|
||||
func (c *Cache) ReadFaults(a Artifact) (faults []Fault, err error) {
|
||||
prefix := Encode(c.Ident(a).Value()) + "."
|
||||
var dents []os.DirEntry
|
||||
if dents, err = os.ReadDir(c.base.Append(dirFault).String()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dent := range dents {
|
||||
name := dent.Name()
|
||||
if !strings.HasPrefix(name, prefix) {
|
||||
continue
|
||||
}
|
||||
var t uint64
|
||||
t, err = strconv.ParseUint(name[len(prefix):], 10, 64)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
faults = append(faults, Fault{c.base.Append(
|
||||
dirFault,
|
||||
name,
|
||||
), t})
|
||||
}
|
||||
|
||||
slices.SortFunc(faults, func(a, b Fault) int {
|
||||
return cmp.Compare(a.t, b.t)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Abort cancels all pending cures and waits for them to clean up, but does not
|
||||
// close the cache.
|
||||
func (c *Cache) Abort() {
|
||||
@@ -2068,9 +2304,11 @@ func open(
|
||||
}
|
||||
|
||||
for _, name := range []string{
|
||||
dirSubstitute,
|
||||
dirIdentifier,
|
||||
dirChecksum,
|
||||
dirStatus,
|
||||
dirFault,
|
||||
dirWork,
|
||||
} {
|
||||
if err := os.MkdirAll(
|
||||
@@ -2093,6 +2331,7 @@ func open(
|
||||
|
||||
irCache: zeroIRCache(),
|
||||
|
||||
substitute: make(map[unique.Handle[ID]]unique.Handle[Checksum]),
|
||||
ident: make(map[unique.Handle[ID]]unique.Handle[Checksum]),
|
||||
identErr: make(map[unique.Handle[ID]]error),
|
||||
identPending: make(map[unique.Handle[ID]]*pendingCure),
|
||||
|
||||
+484
-125
@@ -16,9 +16,12 @@ import (
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
"time"
|
||||
"unique"
|
||||
"unsafe"
|
||||
|
||||
@@ -242,6 +245,41 @@ func newDestroyArtifactFunc(a pkg.Artifact) func(
|
||||
}
|
||||
}
|
||||
|
||||
// destroyStatus counts non-substitution status entries and destroys them.
|
||||
func destroyStatus(t *testing.T, base *check.Absolute, c, s int) {
|
||||
dents, err := os.ReadDir(base.Append("status").String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var gotC, gotS int
|
||||
for _, dent := range dents {
|
||||
if err = os.Remove(base.Append(
|
||||
"status",
|
||||
dent.Name(),
|
||||
).String()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if dent.Type().IsRegular() {
|
||||
gotC++
|
||||
continue
|
||||
}
|
||||
if dent.Type()&fs.ModeSymlink == fs.ModeSymlink {
|
||||
gotS++
|
||||
continue
|
||||
}
|
||||
t.Errorf("%s: %s", dent.Name(), dent.Type())
|
||||
}
|
||||
|
||||
if gotC != c {
|
||||
t.Errorf("status: c = %d, want %d", gotC, c)
|
||||
}
|
||||
if gotS != s {
|
||||
t.Errorf("status: s = %d, want %d", gotS, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdent(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -286,6 +324,99 @@ func TestIdent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// An expectsKnown describes an expected file or directory.
|
||||
type expectsKnown interface {
|
||||
// hash returns the checksum of the represented data.
|
||||
hash() (checksum pkg.Checksum)
|
||||
}
|
||||
|
||||
// An expectsChecksum is a prepared checksum value.
|
||||
type expectsChecksum pkg.Checksum
|
||||
|
||||
// hash returns e.
|
||||
func (e expectsChecksum) hash() pkg.Checksum { return e }
|
||||
|
||||
// An expectsFile is the contents of a file expected by the test suite.
|
||||
type expectsFile []byte
|
||||
|
||||
// hash computes the checksum of e.
|
||||
func (e expectsFile) hash() (checksum pkg.Checksum) {
|
||||
h := sha512.New384()
|
||||
h.Write(e)
|
||||
h.Sum(checksum[:0])
|
||||
return
|
||||
}
|
||||
|
||||
// An expectsFS describes the state of a filesystem expected by the test suite.
|
||||
type expectsFS fstest.MapFS
|
||||
|
||||
// hash computes the checksum of e.
|
||||
func (e expectsFS) hash() (checksum pkg.Checksum) {
|
||||
if err := pkg.HashFS(&checksum, fstest.MapFS(e), "."); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// expectsFrom generates expectsFS for a filesystem directory.
|
||||
func expectsFrom(pathname string) string {
|
||||
var buf strings.Builder
|
||||
buf.WriteString("expectsFS{\n")
|
||||
if err := filepath.WalkDir(pathname, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rel string
|
||||
if rel, err = filepath.Rel(pathname, path); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteString("\t" + strconv.Quote(rel) + ": {Mode: ")
|
||||
|
||||
var fi fs.FileInfo
|
||||
if fi, err = d.Info(); err != nil {
|
||||
return err
|
||||
}
|
||||
mode := fi.Mode()
|
||||
|
||||
switch {
|
||||
case mode.IsDir():
|
||||
buf.WriteString("fs.ModeDir | 0" +
|
||||
strconv.FormatInt(int64(mode&^fs.ModeDir), 8))
|
||||
|
||||
case mode&fs.ModeSymlink != 0:
|
||||
buf.WriteString("fs.ModeSymlink | 0" +
|
||||
strconv.FormatInt(int64(mode&^fs.ModeSymlink), 8) +
|
||||
", Data: []byte(")
|
||||
var linkname string
|
||||
if linkname, err = os.Readlink(path); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteString(strconv.Quote(linkname))
|
||||
buf.WriteByte(')')
|
||||
|
||||
case mode.IsRegular():
|
||||
buf.WriteString("0" + strconv.FormatInt(int64(mode), 8))
|
||||
var p []byte
|
||||
if p, err = os.ReadFile(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
buf.WriteString(", Data: []byte(")
|
||||
buf.WriteString(strconv.Quote(unsafe.String(unsafe.SliceData(p), len(p))))
|
||||
buf.WriteByte(')')
|
||||
}
|
||||
}
|
||||
buf.WriteString("},\n")
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
buf.WriteString("}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// cacheTestCase is a test case passed to checkWithCache where a new instance
|
||||
// of [pkg.Cache] is prepared for the test case, and is validated and removed
|
||||
// on test completion.
|
||||
@@ -294,9 +425,15 @@ type cacheTestCase struct {
|
||||
flags int
|
||||
early func(t *testing.T, base *check.Absolute)
|
||||
f func(t *testing.T, base *check.Absolute, c *pkg.Cache)
|
||||
want pkg.Checksum
|
||||
want expectsFS
|
||||
}
|
||||
|
||||
const (
|
||||
// checkDestroySubstitutes arranges for substitutes to be destroyed before
|
||||
// measurement during checkWithCache.
|
||||
checkDestroySubstitutes = 1 << (iota + 32)
|
||||
)
|
||||
|
||||
// checkWithCache runs a slice of cacheTestCase.
|
||||
func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
||||
t.Helper()
|
||||
@@ -328,7 +465,7 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
||||
msg := message.New(log.New(os.Stderr, "cache: ", 0))
|
||||
msg.SwapVerbose(testing.Verbose())
|
||||
|
||||
flags := tc.flags
|
||||
flags := tc.flags | pkg.CSuppressInit
|
||||
|
||||
if info.CanDegrade {
|
||||
if _, err := landlock.GetABI(); err != nil {
|
||||
@@ -377,19 +514,34 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
||||
}
|
||||
}
|
||||
|
||||
// destroy non-deterministic status files
|
||||
if err := os.RemoveAll(base.Append("status").String()); err != nil {
|
||||
// destroy non-deterministic substitutes
|
||||
if tc.flags&checkDestroySubstitutes != 0 {
|
||||
substitute := base.Append("substitute")
|
||||
if err := os.RemoveAll(substitute.String()); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err = os.Mkdir(substitute.String(), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// destroy empty status directory
|
||||
if err := syscall.Rmdir(base.Append("status").String()); err != nil {
|
||||
t.Error(expectsFrom(base.Append("status").String()))
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// destroy empty fault directory
|
||||
if err := os.Remove(base.Append("fault").String()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := tc.want.hash()
|
||||
|
||||
var checksum pkg.Checksum
|
||||
if err := pkg.HashDir(&checksum, base); err != nil {
|
||||
t.Fatalf("HashDir: error = %v", err)
|
||||
} else if checksum != tc.want {
|
||||
t.Fatalf("HashDir: %v", &pkg.ChecksumMismatchError{
|
||||
Got: checksum,
|
||||
Want: tc.want,
|
||||
})
|
||||
} else if checksum != want {
|
||||
t.Fatal(expectsFrom(base.String()))
|
||||
}
|
||||
|
||||
if err := scrubFunc(); err != nil {
|
||||
@@ -408,10 +560,10 @@ func checkWithCache(t *testing.T, testCases []cacheTestCase) {
|
||||
// validate again to make sure scrub did not condemn anything
|
||||
if err := pkg.HashDir(&checksum, base); err != nil {
|
||||
t.Fatalf("HashDir: error = %v", err)
|
||||
} else if checksum != tc.want {
|
||||
} else if checksum != want {
|
||||
t.Fatalf("(scrubbed) HashDir: %v", &pkg.ChecksumMismatchError{
|
||||
Got: checksum,
|
||||
Want: tc.want,
|
||||
Want: want,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -425,7 +577,7 @@ type cureStep struct {
|
||||
a pkg.Artifact
|
||||
|
||||
pathname *check.Absolute
|
||||
checksum pkg.Checksum
|
||||
output expectsKnown
|
||||
err error
|
||||
}
|
||||
|
||||
@@ -446,17 +598,34 @@ func cureMany(t *testing.T, c *pkg.Cache, steps []cureStep) {
|
||||
for _, step := range steps {
|
||||
t.Log("cure step:", step.name)
|
||||
if pathname, checksum, err := c.Cure(step.a); !reflect.DeepEqual(err, step.err) {
|
||||
faults, _err := c.ReadFaults(step.a)
|
||||
if _err != nil {
|
||||
t.Errorf("ReadFaults: error = %v", _err)
|
||||
}
|
||||
|
||||
var p []byte
|
||||
for _, fault := range faults {
|
||||
p, _err = os.ReadFile(fault.String())
|
||||
if _err != nil {
|
||||
t.Error(_err)
|
||||
continue
|
||||
}
|
||||
t.Log(string(p))
|
||||
t.Logf("faulting cure terminated %s ago", time.Since(faults[0].Time()))
|
||||
}
|
||||
t.Fatalf("Cure: error = %v, want %v", err, step.err)
|
||||
} else if step.pathname != ignorePathname && !pathname.Is(step.pathname) {
|
||||
t.Fatalf("Cure: pathname = %q, want %q", pathname, step.pathname)
|
||||
} else if checksum != makeChecksumH(step.checksum) {
|
||||
if checksum == (unique.Handle[pkg.Checksum]{}) {
|
||||
checksum = unique.Make(pkg.Checksum{})
|
||||
} else if step.output == nil || checksum != makeChecksumH(step.output.hash()) {
|
||||
if pathname != nil {
|
||||
if name, _err := filepath.EvalSymlinks(pathname.String()); _err != nil {
|
||||
t.Fatal(_err)
|
||||
} else {
|
||||
t.Fatal(expectsFrom(name))
|
||||
}
|
||||
} else if checksum != (unique.Handle[pkg.Checksum]{}) {
|
||||
t.Fatalf("Cure: unexpected checksum %s", pkg.Encode(checksum.Value()))
|
||||
}
|
||||
t.Fatalf(
|
||||
"Cure: checksum = %s, want %s",
|
||||
pkg.Encode(checksum.Value()), pkg.Encode(step.checksum),
|
||||
)
|
||||
} else {
|
||||
v := any(err)
|
||||
if err == nil {
|
||||
@@ -517,18 +686,12 @@ func newWantScrubError(base *check.Absolute) *pkg.ScrubError {
|
||||
func TestCache(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const testdata = "" +
|
||||
testdata := expectsFile("" +
|
||||
"\x00\x00\x00\x00" +
|
||||
"\xad\x0b\x00" +
|
||||
"\x04" +
|
||||
"\xfe\xfe\x00\x00" +
|
||||
"\xfe\xca\x00\x00"
|
||||
|
||||
testdataChecksum := func() pkg.Checksum {
|
||||
h := sha512.New384()
|
||||
h.Write([]byte(testdata))
|
||||
return (pkg.Checksum)(h.Sum(nil))
|
||||
}()
|
||||
"\xfe\xca\x00\x00")
|
||||
|
||||
testCases := []cacheTestCase{
|
||||
{"file", pkg.CValidateKnown | pkg.CAssumeChecksum, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
@@ -546,36 +709,45 @@ func TestCache(t *testing.T) {
|
||||
"identifier",
|
||||
"cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe",
|
||||
)
|
||||
failingFile := newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 3},
|
||||
nil,
|
||||
nil, struct {
|
||||
_ []byte
|
||||
stub.UniqueError
|
||||
}{UniqueError: 0xbad},
|
||||
)
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"initial file", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
identifier,
|
||||
&testdataChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), wantPathname, testdataChecksum, nil},
|
||||
new(testdata.hash()),
|
||||
testdata, nil,
|
||||
), wantPathname, testdata, nil},
|
||||
|
||||
{"identical content", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
identifier0,
|
||||
&testdataChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), wantPathname0, testdataChecksum, nil},
|
||||
new(testdata.hash()),
|
||||
testdata, nil,
|
||||
), wantPathname0, testdata, nil},
|
||||
|
||||
{"existing entry", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
identifier,
|
||||
&testdataChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), wantPathname, testdataChecksum, nil},
|
||||
new(testdata.hash()),
|
||||
testdata, nil,
|
||||
), wantPathname, testdata, nil},
|
||||
|
||||
{"checksum mismatch", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 0},
|
||||
new(pkg.Checksum),
|
||||
[]byte(testdata), nil,
|
||||
), nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
||||
Got: testdataChecksum,
|
||||
testdata, nil,
|
||||
), nil, nil, &pkg.ChecksumMismatchError{
|
||||
Got: testdata.hash(),
|
||||
}},
|
||||
|
||||
{"store without validation", newStubFile(
|
||||
@@ -586,7 +758,7 @@ func TestCache(t *testing.T) {
|
||||
), base.Append(
|
||||
"identifier",
|
||||
"vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX",
|
||||
), pkg.Checksum{
|
||||
), expectsChecksum{
|
||||
0xbe, 0xc0, 0x21, 0xb4, 0xf3, 0x68,
|
||||
0xe3, 0x06, 0x91, 0x34, 0xe0, 0x12,
|
||||
0xc2, 0xb4, 0x30, 0x70, 0x83, 0xd3,
|
||||
@@ -600,7 +772,7 @@ func TestCache(t *testing.T) {
|
||||
{"incomplete implementation", struct{ pkg.Artifact }{&stubArtifact{
|
||||
kind: pkg.KindExec,
|
||||
params: []byte("artifact overridden to be incomplete"),
|
||||
}}, nil, pkg.Checksum{}, pkg.InvalidArtifactError(pkg.MustDecode(
|
||||
}}, nil, nil, pkg.InvalidArtifactError(pkg.MustDecode(
|
||||
"E__uZ1sLIvb84vzSm5Uezb03RogsiaeTt1nfIVv8TKnnf4LqwtSi-smdHhlkZrUJ",
|
||||
))},
|
||||
|
||||
@@ -609,40 +781,32 @@ func TestCache(t *testing.T) {
|
||||
pkg.ID{0xff, 1},
|
||||
nil,
|
||||
nil, stub.UniqueError(0xcafe),
|
||||
), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
), nil, nil, stub.UniqueError(0xcafe)},
|
||||
|
||||
{"error caching", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 1},
|
||||
nil,
|
||||
nil, nil,
|
||||
), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
), nil, nil, stub.UniqueError(0xcafe)},
|
||||
|
||||
{"cache hit bad type", overrideChecksum{testdataChecksum, overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{
|
||||
{"cache hit bad type", overrideChecksum{testdata.hash(), overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
}}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||
}}}, nil, nil, pkg.InvalidFileModeError(
|
||||
0400,
|
||||
)},
|
||||
|
||||
{"noncomparable error", &stubArtifactF{
|
||||
kind: pkg.KindExec,
|
||||
params: []byte("artifact with dependency returning noncomparable error"),
|
||||
deps: []pkg.Artifact{newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 3},
|
||||
nil,
|
||||
nil, struct {
|
||||
_ []byte
|
||||
stub.UniqueError
|
||||
}{UniqueError: 0xbad},
|
||||
)},
|
||||
deps: []pkg.Artifact{failingFile},
|
||||
|
||||
cure: func(f *pkg.FContext) error {
|
||||
panic("attempting to cure impossible artifact")
|
||||
},
|
||||
}, nil, pkg.Checksum{}, &pkg.DependencyCureError{
|
||||
}, nil, nil, &pkg.DependencyCureError{
|
||||
{
|
||||
Ident: unique.Make(pkg.ID{0xff, 3}),
|
||||
A: failingFile,
|
||||
Err: struct {
|
||||
_ []byte
|
||||
stub.UniqueError
|
||||
@@ -662,18 +826,18 @@ func TestCache(t *testing.T) {
|
||||
cureMany(t, c0, []cureStep{
|
||||
{"cache hit ident", overrideIdent{
|
||||
id: identifier,
|
||||
}, wantPathname, testdataChecksum, nil},
|
||||
}, wantPathname, testdata, nil},
|
||||
|
||||
{"cache miss checksum match", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
testdataChecksum,
|
||||
testdata.hash(),
|
||||
nil,
|
||||
[]byte(testdata),
|
||||
testdata,
|
||||
nil,
|
||||
), base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(testdataChecksum),
|
||||
), testdataChecksum, nil},
|
||||
pkg.Encode(testdata.hash()),
|
||||
), testdata, nil},
|
||||
})
|
||||
|
||||
// cure after close
|
||||
@@ -686,7 +850,23 @@ func TestCache(t *testing.T) {
|
||||
t.Fatalf("(closed) Cure: error = %v", err)
|
||||
}
|
||||
}
|
||||
}, pkg.MustDecode("St9rlE-mGZ5gXwiv_hzQ_B8bZP-UUvSNmf4nHUZzCMOumb6hKnheZSe0dmnuc4Q2")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX": {Mode: 0400, Data: []byte{0}},
|
||||
"checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq": {Mode: 0400, Data: []byte{0, 0, 0, 0, 0xad, 0xb, 0, 4, 0xfe, 0xfe, 0, 0, 0xfe, 0xca, 0, 0}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/vsAhtPNo4waRNOASwrQwcIPTqb3SBuJOXw2G4T1mNmVZM-wrQTRllmgXqcIIoRcX")},
|
||||
"identifier/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
"identifier/cafebabecafebabecafebabecafebabecafebabecafebabecafebabecafebabe": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
"identifier/deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/0bSFPu5Tnd-2Jj0Mv6co23PW2t3BmHc7eLFj9TgY3eIBg8zislo7xZYNBqovVLcq")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"directory", pkg.CAssumeChecksum, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
id := pkg.MustDecode(
|
||||
@@ -721,9 +901,16 @@ func TestCache(t *testing.T) {
|
||||
).String(),
|
||||
)
|
||||
}
|
||||
wantChecksum := pkg.MustDecode(
|
||||
"qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b",
|
||||
)
|
||||
want := expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
|
||||
"lib": {Mode: fs.ModeDir | 0700},
|
||||
"lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"lib/pkgconfig": {Mode: fs.ModeDir | 0700},
|
||||
}
|
||||
wantPathname := base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(id),
|
||||
@@ -777,33 +964,33 @@ func TestCache(t *testing.T) {
|
||||
}
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"initial directory", overrideChecksum{wantChecksum, overrideIdent{id, &stubArtifact{
|
||||
{"initial directory", overrideChecksum{want.hash(), overrideIdent{id, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: makeSample,
|
||||
}}}, wantPathname, wantChecksum, nil},
|
||||
}}}, wantPathname, want, nil},
|
||||
|
||||
{"identical identifier", overrideChecksum{wantChecksum, overrideIdent{id, &stubArtifact{
|
||||
{"identical identifier", overrideChecksum{want.hash(), overrideIdent{id, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
}}}, wantPathname, wantChecksum, nil},
|
||||
}}}, wantPathname, want, nil},
|
||||
|
||||
{"identical checksum", overrideIdent{id0, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: makeSample,
|
||||
}}, wantPathname0, wantChecksum, nil},
|
||||
}}, wantPathname0, want, nil},
|
||||
|
||||
{"cure fault", overrideIdent{pkg.ID{0xff, 0}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return makeGarbage(t.GetWorkDir(), stub.UniqueError(0xcafe))
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
}}, nil, nil, stub.UniqueError(0xcafe)},
|
||||
|
||||
{"checksum mismatch", overrideChecksum{pkg.Checksum{}, overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return makeGarbage(t.GetWorkDir(), nil)
|
||||
},
|
||||
}}}, nil, pkg.Checksum{}, &pkg.ChecksumMismatchError{
|
||||
}}}, nil, nil, &pkg.ChecksumMismatchError{
|
||||
Got: pkg.MustDecode(
|
||||
"CUx-3hSbTWPsbMfDhgalG4Ni_GmR9TnVX8F99tY_P5GtkYvczg9RrF5zO0jX9XYT",
|
||||
),
|
||||
@@ -812,27 +999,27 @@ func TestCache(t *testing.T) {
|
||||
{"cache hit bad type", newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 2},
|
||||
&wantChecksum,
|
||||
[]byte(testdata), nil,
|
||||
), nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||
new(want.hash()),
|
||||
testdata, nil,
|
||||
), nil, nil, pkg.InvalidFileModeError(
|
||||
fs.ModeDir | 0500,
|
||||
)},
|
||||
|
||||
{"openFile directory", overrideIdent{pkg.ID{0xff, 3}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
r, err := t.Open(overrideChecksumFile{checksum: wantChecksum})
|
||||
r, err := t.Open(overrideChecksumFile{checksum: want.hash()})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = io.ReadAll(r)
|
||||
return err
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, &os.PathError{
|
||||
}}, nil, nil, &os.PathError{
|
||||
Op: "read",
|
||||
Path: base.Append(
|
||||
"checksum",
|
||||
pkg.Encode(wantChecksum),
|
||||
pkg.Encode(want.hash()),
|
||||
).String(),
|
||||
Err: syscall.EISDIR,
|
||||
}},
|
||||
@@ -842,14 +1029,14 @@ func TestCache(t *testing.T) {
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return nil
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, pkg.NoOutputError{}},
|
||||
}}, nil, nil, pkg.NoOutputError{}},
|
||||
|
||||
{"file output", overrideIdent{pkg.ID{0xff, 5}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return os.WriteFile(t.GetWorkDir().String(), []byte{0}, 0400)
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, errors.New("non-file artifact produced regular file")},
|
||||
}}, nil, nil, errors.New("non-file artifact produced regular file")},
|
||||
|
||||
{"symlink output", overrideIdent{pkg.ID{0xff, 6}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
@@ -859,11 +1046,111 @@ func TestCache(t *testing.T) {
|
||||
t.GetWorkDir().String(),
|
||||
)
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, pkg.InvalidFileModeError(
|
||||
}}, nil, nil, pkg.InvalidFileModeError(
|
||||
fs.ModeSymlink | 0777,
|
||||
)},
|
||||
|
||||
{"alternative", &stubArtifactF{
|
||||
kind: pkg.KindExec,
|
||||
params: []byte("substitutable artifact"),
|
||||
deps: []pkg.Artifact{newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 8},
|
||||
nil,
|
||||
[]byte("substitutable dependency"),
|
||||
nil,
|
||||
)},
|
||||
|
||||
cure: func(f *pkg.FContext) error {
|
||||
return makeSample(&f.TContext)
|
||||
},
|
||||
}, base.Append(
|
||||
"identifier",
|
||||
"xMDWovje7OfyIaDy_2VnjpKxRqSOQ_LoeD946t-3WsS2V2SeMJ7nDGrNfpa4Pbc-",
|
||||
), want, nil},
|
||||
|
||||
{"substitutable", &stubArtifactF{
|
||||
kind: pkg.KindExec,
|
||||
params: []byte("substitutable artifact"),
|
||||
deps: []pkg.Artifact{newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 10},
|
||||
nil,
|
||||
[]byte("substitutable dependency"),
|
||||
nil,
|
||||
)},
|
||||
|
||||
cure: func(f *pkg.FContext) error {
|
||||
panic("substitution missed")
|
||||
},
|
||||
}, base.Append(
|
||||
"identifier",
|
||||
"k2ilgG5KQ9NXnMoT2oB6NdwOnSPRn_H24oXQc4l6qOYIxIG9XfuEczeyrR8UEv_f",
|
||||
), want, nil},
|
||||
})
|
||||
}, pkg.MustDecode("WVpvsVqVKg9Nsh744x57h51AuWUoUR2nnh8Md-EYBQpk6ziyTuUn6PLtF2e0Eu_d")},
|
||||
|
||||
if c0, err := unsafeOpen(
|
||||
t.Context(),
|
||||
message.New(nil),
|
||||
0, 0, 0, base, false,
|
||||
); err != nil {
|
||||
t.Fatalf("open: error = %v", err)
|
||||
} else {
|
||||
t.Cleanup(c.Close)
|
||||
cureMany(t, c0, []cureStep{
|
||||
{"substitutable", &stubArtifactF{
|
||||
kind: pkg.KindExec,
|
||||
params: []byte("substitutable artifact"),
|
||||
deps: []pkg.Artifact{newStubFile(
|
||||
pkg.KindHTTPGet,
|
||||
pkg.ID{0xff, 0xff, 0xfd, 0xfd},
|
||||
nil,
|
||||
[]byte("substitutable dependency"),
|
||||
nil,
|
||||
)},
|
||||
|
||||
cure: func(f *pkg.FContext) error {
|
||||
panic("substitution missed")
|
||||
},
|
||||
}, base.Append(
|
||||
"identifier",
|
||||
"_EmV5nsYZ2UWHgRmLDMU8i-rJWDx-kv5_1pFrzQI7vMMCM5mAXivO8UZtVfOqMR_",
|
||||
), want, nil},
|
||||
})
|
||||
}
|
||||
|
||||
if dents, err := os.ReadDir(base.Append("status").String()); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if len(dents) > 0 {
|
||||
t.Errorf("ReadDir: %v", dents)
|
||||
}
|
||||
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO": {Mode: 0400, Data: []byte("substitutable dependency")},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib/pkgconfig": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b/lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
"identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
"identifier/_EmV5nsYZ2UWHgRmLDMU8i-rJWDx-kv5_1pFrzQI7vMMCM5mAXivO8UZtVfOqMR_": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
"identifier/___9_QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")},
|
||||
"identifier/_wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")},
|
||||
"identifier/_woAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/7lfQ4QwSpV8nw7IDh0JiQ_jqUPrPv3_Vfie034RxsSy-cy4vO8DVvxgpx2LW08oO")},
|
||||
"identifier/k2ilgG5KQ9NXnMoT2oB6NdwOnSPRn_H24oXQc4l6qOYIxIG9XfuEczeyrR8UEv_f": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
"identifier/xMDWovje7OfyIaDy_2VnjpKxRqSOQ_LoeD946t-3WsS2V2SeMJ7nDGrNfpa4Pbc-": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"substitute/OyBGorh72Z9kVw35JUa8FbqDbpR4DqT-MX1jic0uKN5PdYmUBiAF38BRsIRnBigf": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/qRN6in76LndiiOZJheHkwyW8UT1N5-f-bXvHfDvwrMw2fSkOoZdh8pWE1qhLk65b")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"pending", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
wantErr := stub.UniqueError(0xcafe)
|
||||
@@ -899,7 +1186,7 @@ func TestCache(t *testing.T) {
|
||||
pkg.ID{0xff, 1},
|
||||
nil,
|
||||
nil, stub.UniqueError(0xbad),
|
||||
), nil, pkg.Checksum{}, stub.UniqueError(0xbad)},
|
||||
), nil, nil, stub.UniqueError(0xbad)},
|
||||
|
||||
{"file output", overrideIdent{pkg.ID{0xff, 2}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
@@ -910,7 +1197,7 @@ func TestCache(t *testing.T) {
|
||||
0400,
|
||||
)
|
||||
},
|
||||
}}, nil, pkg.Checksum{}, errors.New(
|
||||
}}, nil, nil, errors.New(
|
||||
"non-file artifact produced regular file",
|
||||
)},
|
||||
})
|
||||
@@ -932,7 +1219,13 @@ func TestCache(t *testing.T) {
|
||||
for c.Done(unique.Make(pkg.ID{0xff})) != nil {
|
||||
}
|
||||
<-wCureDone
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"cancel abort block", pkg.CValidateKnown, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
var wg sync.WaitGroup
|
||||
@@ -985,7 +1278,13 @@ func TestCache(t *testing.T) {
|
||||
|
||||
c.Close()
|
||||
c.Abort()
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"no assume checksum", 0, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
makeGarbage := func(work *check.Absolute, wantErr error) error {
|
||||
@@ -1002,10 +1301,12 @@ func TestCache(t *testing.T) {
|
||||
return wantErr
|
||||
}
|
||||
|
||||
wantChecksum := pkg.MustDecode("Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")
|
||||
want := expectsChecksum(pkg.MustDecode(
|
||||
"Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M",
|
||||
))
|
||||
|
||||
cureMany(t, c, []cureStep{
|
||||
{"create", overrideChecksum{wantChecksum, overrideIdent{pkg.ID{0xff, 0}, &stubArtifact{
|
||||
{"create", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xff, 0}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return makeGarbage(t.GetWorkDir(), nil)
|
||||
@@ -1013,16 +1314,16 @@ func TestCache(t *testing.T) {
|
||||
}}}, base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(pkg.ID{0xff, 0}),
|
||||
), wantChecksum, nil},
|
||||
), want, nil},
|
||||
|
||||
{"reject", overrideChecksum{wantChecksum, overrideIdent{pkg.ID{0xfe, 1}, &stubArtifact{
|
||||
{"reject", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xfe, 1}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return makeGarbage(t.GetWorkDir(), stub.UniqueError(0xbad))
|
||||
},
|
||||
}}}, nil, pkg.Checksum{}, stub.UniqueError(0xbad)},
|
||||
}}}, nil, nil, stub.UniqueError(0xbad)},
|
||||
|
||||
{"match", overrideChecksum{wantChecksum, overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{
|
||||
{"match", overrideChecksum{want.hash(), overrideIdent{pkg.ID{0xff, 1}, &stubArtifact{
|
||||
kind: pkg.KindTar,
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return makeGarbage(t.GetWorkDir(), nil)
|
||||
@@ -1030,9 +1331,23 @@ func TestCache(t *testing.T) {
|
||||
}}}, base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(pkg.ID{0xff, 1}),
|
||||
), wantChecksum, nil},
|
||||
), want, nil},
|
||||
})
|
||||
}, pkg.MustDecode("OC290t23aimNo2Rp2pPwan5GI2KRLRdOwYxXQMD9jw0QROgHnNXWodoWdV0hwu2w")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M/check": {Mode: 0400, Data: []byte{}},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/_wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")},
|
||||
"identifier/_wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/Aubi5EG4_Y8DhL9bQ3Q4HFBhLRF7X5gt9D3CNCQfT-TeBtlRXc7Zi_JYZEMoCC7M")},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"scrub", 0, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
cureMany(t, c, []cureStep{
|
||||
@@ -1044,7 +1359,7 @@ func TestCache(t *testing.T) {
|
||||
), base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(pkg.Checksum{0xfe, 0}),
|
||||
), pkg.Checksum{0xff, 0}, nil},
|
||||
), expectsChecksum{0xff, 0}, nil},
|
||||
})
|
||||
|
||||
for _, p := range [][]string{
|
||||
@@ -1077,7 +1392,13 @@ func TestCache(t *testing.T) {
|
||||
if err := c.Scrub(1 << 6); !reflect.DeepEqual(err, wantErr) {
|
||||
t.Fatalf("Scrub: error =\n%s\nwant\n%s", err, wantErr)
|
||||
}
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
}
|
||||
checkWithCache(t, testCases)
|
||||
}
|
||||
@@ -1135,6 +1456,11 @@ func TestErrors(t *testing.T) {
|
||||
{"UnsupportedVariantError", pkg.UnsupportedVariantError(
|
||||
"rosa",
|
||||
), `unsupported variant "rosa"`},
|
||||
|
||||
{"UnsupportedArchError zero", pkg.UnsupportedArchError(""),
|
||||
"invalid architecture name"},
|
||||
{"UnsupportedArchError", pkg.UnsupportedArchError("riscv64"),
|
||||
"unsupported architecture riscv64"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -1250,6 +1576,14 @@ errors during scrub:
|
||||
func TestDependencyCureError(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
makeIdent := func(ident ...byte) pkg.Artifact {
|
||||
var a overrideIdent
|
||||
copy(a.id[:], ident)
|
||||
// does not compare equal
|
||||
a.TrivialArtifact = new(stubArtifact)
|
||||
return a
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
err pkg.DependencyCureError
|
||||
@@ -1257,51 +1591,51 @@ func TestDependencyCureError(t *testing.T) {
|
||||
unwrap []error
|
||||
}{
|
||||
{"simple", pkg.DependencyCureError{
|
||||
{Ident: unique.Make(pkg.ID{0xff, 9}), Err: stub.UniqueError(0xbad09)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xf}), Err: stub.UniqueError(0xbad0f)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 1}), Err: stub.UniqueError(0xbad01)},
|
||||
{A: makeIdent(0xff, 9), Err: stub.UniqueError(0xbad09)},
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 0xf), Err: stub.UniqueError(0xbad0f)},
|
||||
{A: makeIdent(0xff, 1), Err: stub.UniqueError(0xbad01)},
|
||||
}, `errors curing dependencies:
|
||||
_wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765184 injected by the test suite
|
||||
_wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765185 injected by the test suite
|
||||
_wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765193 injected by the test suite
|
||||
_w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765199 injected by the test suite`, []error{
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 1}), Err: stub.UniqueError(0xbad01)},
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 9}), Err: stub.UniqueError(0xbad09)},
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 0xf}), Err: stub.UniqueError(0xbad0f)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 1), Err: stub.UniqueError(0xbad01)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 9), Err: stub.UniqueError(0xbad09)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 0xf), Err: stub.UniqueError(0xbad0f)},
|
||||
}},
|
||||
|
||||
{"dedup", pkg.DependencyCureError{
|
||||
{Ident: unique.Make(pkg.ID{0xff, 9}), Err: stub.UniqueError(0xbad09)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xfd}), Err: &pkg.DependencyCureError{
|
||||
{Ident: unique.Make(pkg.ID{0xff, 9}), Err: stub.UniqueError(0xbad09)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xc}), Err: &pkg.DependencyCureError{
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xf}), Err: stub.UniqueError(0xbad0f)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 9), Err: stub.UniqueError(0xbad09)},
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 0xfd), Err: &pkg.DependencyCureError{
|
||||
{A: makeIdent(0xff, 9), Err: stub.UniqueError(0xbad09)},
|
||||
{A: makeIdent(0xff, 0xc), Err: &pkg.DependencyCureError{
|
||||
{A: makeIdent(0xff, 0xf), Err: stub.UniqueError(0xbad0f)},
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
}},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
}},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xff}), Err: &pkg.DependencyCureError{
|
||||
{Ident: unique.Make(pkg.ID{0xff, 9}), Err: stub.UniqueError(0xbad09)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xc}), Err: &pkg.DependencyCureError{
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 0xff), Err: &pkg.DependencyCureError{
|
||||
{A: makeIdent(0xff, 9), Err: stub.UniqueError(0xbad09)},
|
||||
{A: makeIdent(0xff, 0xc), Err: &pkg.DependencyCureError{
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
}},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
}},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 0xf}), Err: stub.UniqueError(0xbad0f)},
|
||||
{Ident: unique.Make(pkg.ID{0xff, 1}), Err: stub.UniqueError(0xbad01)},
|
||||
{A: makeIdent(0xff, 0xf), Err: stub.UniqueError(0xbad0f)},
|
||||
{A: makeIdent(0xff, 1), Err: stub.UniqueError(0xbad01)},
|
||||
}, `errors curing dependencies:
|
||||
_wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765184 injected by the test suite
|
||||
_wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765185 injected by the test suite
|
||||
_wkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765193 injected by the test suite
|
||||
_w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA: unique error 765199 injected by the test suite`, []error{
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 0}), Err: stub.UniqueError(0xbad00)},
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 1}), Err: stub.UniqueError(0xbad01)},
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 9}), Err: stub.UniqueError(0xbad09)},
|
||||
&pkg.CureError{Ident: unique.Make(pkg.ID{0xff, 0xf}), Err: stub.UniqueError(0xbad0f)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 0), Err: stub.UniqueError(0xbad00)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 1), Err: stub.UniqueError(0xbad01)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 9), Err: stub.UniqueError(0xbad09)},
|
||||
&pkg.CureError{A: makeIdent(0xff, 0xf), Err: stub.UniqueError(0xbad0f)},
|
||||
}},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
@@ -1342,6 +1676,25 @@ func (a earlyFailureF) Cure(*pkg.FContext) error {
|
||||
return stub.UniqueError(0xcafe)
|
||||
}
|
||||
|
||||
func BenchmarkEarlyDCE(b *testing.B) {
|
||||
msg := message.New(log.New(os.Stderr, "dce: ", 0))
|
||||
msg.SwapVerbose(testing.Verbose())
|
||||
c, err := pkg.Open(b.Context(), msg, 0, 0, 0, check.MustAbs(b.TempDir()))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
_, _, err = c.Cure(earlyFailureF(8))
|
||||
if !errors.Is(err, stub.UniqueError(0xcafe)) {
|
||||
b.Fatalf("Cure: error = %v", err)
|
||||
}
|
||||
c.Close()
|
||||
dce := err.(*pkg.DependencyCureError)
|
||||
|
||||
for b.Loop() {
|
||||
dce.Unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependencyCureErrorEarly(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -1351,7 +1704,13 @@ func TestDependencyCureErrorEarly(t *testing.T) {
|
||||
if !errors.Is(err, stub.UniqueError(0xcafe)) {
|
||||
t.Fatalf("Cure: error = %v", err)
|
||||
}
|
||||
}, pkg.MustDecode("E4vEZKhCcL2gPZ2Tt59FS3lDng-d_2SKa2i5G_RbDfwGn6EemptFaGLPUDiOa94C")},
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+74
-14
@@ -20,6 +20,31 @@ import (
|
||||
func TestTar(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
want := expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/pkgconfig": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0500},
|
||||
"identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
"identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0500},
|
||||
}
|
||||
wantEncode := pkg.Encode(want.hash())
|
||||
|
||||
wantExpand := expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
}
|
||||
wantExpandEncode := pkg.Encode(wantExpand.hash())
|
||||
|
||||
checkWithCache(t, []cacheTestCase{
|
||||
{"http", 0, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
checkTarHTTP(t, base, c, fstest.MapFS{
|
||||
@@ -37,10 +62,32 @@ func TestTar(t *testing.T) {
|
||||
"identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}, pkg.MustDecode(
|
||||
"cTw0h3AmYe7XudSoyEMByduYXqGi-N5ZkTZ0t9K5elsu3i_jNIVF5T08KR1roBFM",
|
||||
))
|
||||
}, pkg.MustDecode("NQTlc466JmSVLIyWklm_u8_g95jEEb98PxJU-kjwxLpfdjwMWJq0G8ze9R4Vo1Vu")},
|
||||
}, want)
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantEncode + "/checksum": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantEncode + "/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantEncode + "/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/check": {Mode: 0400, Data: []byte{0, 0}},
|
||||
"checksum/" + wantEncode + "/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantEncode + "/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
"checksum/" + wantEncode + "/checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP/lib/pkgconfig": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantEncode + "/identifier": {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantEncode + "/identifier/HnySzeLQvSBZuTUcvfmLEX_OmH4yJWWH788NxuLuv7kVn8_uPM6Ks4rqFWM2NZJY": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
"checksum/" + wantEncode + "/identifier/Zx5ZG9BAwegNT3zQwCySuI2ktCXxNgxirkGLFjW4FW06PtojYVaCdtEw8yuntPLa": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/1TL00Qb8dcqayX7wTO8WNaraHvY6b-KCsctLDTrb64QBCmxj_-byK1HdIUwMaFEP")},
|
||||
"checksum/" + wantEncode + "/work": {Mode: fs.ModeDir | 0500},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/W5S65DEhawz_WKaok5NjUKLmnD9dNl5RPauNJjcOVcB3VM4eGhSaLGmXbL8vZpiw": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantEncode)},
|
||||
"identifier/rg7F1D5hwv6o4xctjD5zDq4i5MD0mArTsUIWfhUbik8xC6Bsyt3mjXXOm3goojTz": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantEncode)},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
|
||||
{"http expand", 0, nil, func(t *testing.T, base *check.Absolute, c *pkg.Cache) {
|
||||
checkTarHTTP(t, base, c, fstest.MapFS{
|
||||
@@ -48,10 +95,23 @@ func TestTar(t *testing.T) {
|
||||
|
||||
"lib": {Mode: fs.ModeDir | 0700},
|
||||
"lib/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
}, pkg.MustDecode(
|
||||
"CH3AiUrCCcVOjOYLaMKKK1Da78989JtfHeIsxMzWOQFiN4mrCLDYpoDxLWqJWCUN",
|
||||
))
|
||||
}, pkg.MustDecode("hSoSSgCYTNonX3Q8FjvjD1fBl-E-BQyA6OTXro2OadXqbST4tZ-akGXszdeqphRe")},
|
||||
}, wantExpand)
|
||||
}, expectsFS{
|
||||
".": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"checksum": {Mode: fs.ModeDir | 0700},
|
||||
"checksum/" + wantExpandEncode: {Mode: fs.ModeDir | 0500},
|
||||
"checksum/" + wantExpandEncode + "/libedac.so": {Mode: fs.ModeSymlink | 0777, Data: []byte("/proc/nonexistent/libedac.so")},
|
||||
|
||||
"identifier": {Mode: fs.ModeDir | 0700},
|
||||
"identifier/W5S65DEhawz_WKaok5NjUKLmnD9dNl5RPauNJjcOVcB3VM4eGhSaLGmXbL8vZpiw": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantExpandEncode)},
|
||||
"identifier/_v1blm2h-_KA-dVaawdpLas6MjHc6rbhhFS8JWwx8iJxZGUu8EBbRrhr5AaZ9PJL": {Mode: fs.ModeSymlink | 0777, Data: []byte("../checksum/" + wantExpandEncode)},
|
||||
|
||||
"substitute": {Mode: fs.ModeDir | 0700},
|
||||
|
||||
"temp": {Mode: fs.ModeDir | 0700},
|
||||
"work": {Mode: fs.ModeDir | 0700},
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,7 +120,7 @@ func checkTarHTTP(
|
||||
base *check.Absolute,
|
||||
c *pkg.Cache,
|
||||
testdataFsys fs.FS,
|
||||
wantChecksum pkg.Checksum,
|
||||
want expectsKnown,
|
||||
) {
|
||||
var testdata string
|
||||
{
|
||||
@@ -194,24 +254,24 @@ func checkTarHTTP(
|
||||
{"file", a, base.Append(
|
||||
"identifier",
|
||||
pkg.Encode(wantIdent),
|
||||
), wantChecksum, nil},
|
||||
), want, nil},
|
||||
|
||||
{"directory", pkg.NewTar(
|
||||
&tarDir,
|
||||
pkg.TarGzip,
|
||||
), ignorePathname, wantChecksum, nil},
|
||||
), ignorePathname, want, nil},
|
||||
|
||||
{"multiple entries", pkg.NewTar(
|
||||
&tarDirMulti,
|
||||
pkg.TarGzip,
|
||||
), nil, pkg.Checksum{}, errors.New(
|
||||
), nil, nil, errors.New(
|
||||
"input directory does not contain a single regular file",
|
||||
)},
|
||||
|
||||
{"bad type", pkg.NewTar(
|
||||
&tarDirType,
|
||||
pkg.TarGzip,
|
||||
), nil, pkg.Checksum{}, errors.New(
|
||||
), nil, nil, errors.New(
|
||||
"input directory does not contain a single regular file",
|
||||
)},
|
||||
|
||||
@@ -221,6 +281,6 @@ func checkTarHTTP(
|
||||
cure: func(t *pkg.TContext) error {
|
||||
return stub.UniqueError(0xcafe)
|
||||
},
|
||||
}, pkg.TarGzip), nil, pkg.Checksum{}, stub.UniqueError(0xcafe)},
|
||||
}, pkg.TarGzip), nil, nil, stub.UniqueError(0xcafe)},
|
||||
})
|
||||
}
|
||||
|
||||
+85
-4
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@@ -44,6 +45,7 @@ const (
|
||||
Binutils
|
||||
Bison
|
||||
Bzip2
|
||||
Cairo
|
||||
CMake
|
||||
Connman
|
||||
Coreutils
|
||||
@@ -55,7 +57,12 @@ const (
|
||||
Fakeroot
|
||||
Findutils
|
||||
Flex
|
||||
FontConfig
|
||||
FontUtil
|
||||
Freetype
|
||||
Fribidi
|
||||
Fuse
|
||||
Gconf
|
||||
GMP
|
||||
GLib
|
||||
Gawk
|
||||
@@ -65,39 +72,77 @@ const (
|
||||
Glslang
|
||||
GnuTLS
|
||||
Go
|
||||
GObjectIntrospection
|
||||
Gperf
|
||||
Grep
|
||||
Gzip
|
||||
Hakurei
|
||||
HakureiDist
|
||||
HarfBuzz
|
||||
Hwdata
|
||||
IPTables
|
||||
JDK
|
||||
Kmod
|
||||
LIT
|
||||
LibX11
|
||||
LibXau
|
||||
LibXdmcp
|
||||
LibXext
|
||||
LibXfixes
|
||||
LibXfont2
|
||||
LibXft
|
||||
LibXi
|
||||
LibXrandr
|
||||
LibXrender
|
||||
LibXtst
|
||||
LibXxf86vm
|
||||
Libarchive
|
||||
Libbsd
|
||||
Libcap
|
||||
Libconfig
|
||||
Libdatrie
|
||||
LibdisplayInfo
|
||||
Libdrm
|
||||
Libepoxy
|
||||
Libev
|
||||
Libexpat
|
||||
Libffi
|
||||
Libfontenc
|
||||
Libgd
|
||||
Libglvnd
|
||||
Libiconv
|
||||
Libjpeg
|
||||
Libmd
|
||||
Libmnl
|
||||
Libpciaccess
|
||||
Libnftnl
|
||||
Libpciaccess
|
||||
Libpng
|
||||
Libpsl
|
||||
Libseccomp
|
||||
Libtasn1
|
||||
Libthai
|
||||
Libtirpc
|
||||
Libtool
|
||||
Libucontext
|
||||
Libunistring
|
||||
Libva
|
||||
LibxcbRenderUtil
|
||||
LibxcbUtil
|
||||
LibxcbUtilImage
|
||||
LibxcbUtilKeysyms
|
||||
LibxcbUtilWM
|
||||
Libxcvt
|
||||
Libxkbfile
|
||||
Libxml2
|
||||
Libxshmfence
|
||||
Libxslt
|
||||
Libxtrans
|
||||
LMSensors
|
||||
M4
|
||||
MPC
|
||||
MPFR
|
||||
Make
|
||||
Mesa
|
||||
Meson
|
||||
Mksh
|
||||
MuslFts
|
||||
@@ -110,6 +155,7 @@ const (
|
||||
OpenSSL
|
||||
P11Kit
|
||||
PCRE2
|
||||
Pango
|
||||
Parallel
|
||||
Patch
|
||||
Perl
|
||||
@@ -119,10 +165,12 @@ const (
|
||||
PerlPodParser
|
||||
PerlSGMLS
|
||||
PerlTermReadKey
|
||||
PerlTestCmd
|
||||
PerlTextCharWidth
|
||||
PerlTextWrapI18N
|
||||
PerlUnicodeLineBreak
|
||||
PerlYAMLTiny
|
||||
Pixman
|
||||
PkgConfig
|
||||
Procps
|
||||
Python
|
||||
@@ -136,6 +184,7 @@ const (
|
||||
PythonPluggy
|
||||
PythonPyTest
|
||||
PythonPyYAML
|
||||
PythonPycparser
|
||||
PythonPygments
|
||||
PythonSetuptools
|
||||
PythonSetuptoolsSCM
|
||||
@@ -147,7 +196,9 @@ const (
|
||||
Readline
|
||||
Rsync
|
||||
Sed
|
||||
SharedMimeInfo
|
||||
SPIRVHeaders
|
||||
SPIRVLLVMTranslator
|
||||
SPIRVTools
|
||||
SquashfsTools
|
||||
Strace
|
||||
@@ -158,20 +209,39 @@ const (
|
||||
toyboxEarly
|
||||
Unzip
|
||||
UtilLinux
|
||||
VIM
|
||||
Wayland
|
||||
WaylandProtocols
|
||||
XCB
|
||||
XCBProto
|
||||
XDGDBusProxy
|
||||
XZ
|
||||
Xproto
|
||||
Xkbcomp
|
||||
XkeyboardConfig
|
||||
XorgProto
|
||||
Xserver
|
||||
Zip
|
||||
Zlib
|
||||
Zstd
|
||||
|
||||
// PresetUnexportedStart is the first unexported preset.
|
||||
PresetUnexportedStart
|
||||
|
||||
buildcatrust = iota - 1
|
||||
stage0Dist = iota - 1
|
||||
llvmSource
|
||||
// earlyCompilerRT is an early, standalone compiler-rt installation for the
|
||||
// standalone runtimes build.
|
||||
//
|
||||
// earlyCompilerRT must only be loaded by [LLVM].
|
||||
earlyCompilerRT
|
||||
// earlyRuntimes is an early, standalone installation of LLVM runtimes to
|
||||
// work around the cmake build system leaking the system LLVM installation
|
||||
// when invoking the newly built toolchain.
|
||||
//
|
||||
// earlyRuntimes must only be loaded by [LLVM].
|
||||
earlyRuntimes
|
||||
|
||||
buildcatrust
|
||||
utilMacros
|
||||
|
||||
// Musl is a standalone libc that does not depend on the toolchain.
|
||||
@@ -323,6 +393,9 @@ var (
|
||||
// artifactsOnce is for lazy initialisation of artifacts.
|
||||
artifactsOnce [_toolchainEnd][len(artifactsM)]sync.Once
|
||||
|
||||
// arch is the target architecture.
|
||||
arch = runtime.GOARCH
|
||||
|
||||
// presetOpts globally modifies behaviour of presets.
|
||||
presetOpts int
|
||||
)
|
||||
@@ -334,6 +407,9 @@ const (
|
||||
OptLLVMNoLTO
|
||||
)
|
||||
|
||||
// Arch returns the target architecture.
|
||||
func Arch() string { return arch }
|
||||
|
||||
// Flags returns the current preset flags
|
||||
func Flags() int { return presetOpts }
|
||||
|
||||
@@ -343,7 +419,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
|
||||
// it returns. Must not be used concurrently with any other function from this
|
||||
// package.
|
||||
func DropCaches(flags int) {
|
||||
func DropCaches(targetArch string, flags int) {
|
||||
if targetArch == "" {
|
||||
targetArch = runtime.GOARCH
|
||||
}
|
||||
|
||||
arch = targetArch
|
||||
presetOpts = flags
|
||||
zero(&artifacts)
|
||||
zero(&artifactsOnce)
|
||||
|
||||
@@ -20,8 +20,8 @@ func TestLoad(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkAll(b *testing.B) {
|
||||
flags := rosa.Flags()
|
||||
b.Cleanup(func() { rosa.DropCaches(flags) })
|
||||
arch, flags := rosa.Arch(), rosa.Flags()
|
||||
b.Cleanup(func() { rosa.DropCaches(arch, flags) })
|
||||
|
||||
for b.Loop() {
|
||||
for i := range rosa.PresetEnd {
|
||||
@@ -29,7 +29,7 @@ func BenchmarkAll(b *testing.B) {
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
rosa.DropCaches(0)
|
||||
rosa.DropCaches("", 0)
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"hakurei.app/fhs"
|
||||
@@ -88,7 +87,7 @@ func (a busyboxBin) Cure(t *pkg.TContext) (err error) {
|
||||
// the https://busybox.net/downloads/binaries/ binary release.
|
||||
func newBusyboxBin() pkg.Artifact {
|
||||
var version, url, checksum string
|
||||
switch runtime.GOARCH {
|
||||
switch arch {
|
||||
case "amd64":
|
||||
version = "1.35.0"
|
||||
url = "https://busybox.net/downloads/binaries/" +
|
||||
@@ -101,11 +100,11 @@ func newBusyboxBin() pkg.Artifact {
|
||||
checksum = "npJjBO7iwhjW6Kx2aXeSxf8kXhVgTCDChOZTTsI8ZfFfa3tbsklxRiidZQdrVERg"
|
||||
|
||||
default:
|
||||
panic("unsupported target " + runtime.GOARCH)
|
||||
panic("unsupported target " + arch)
|
||||
}
|
||||
|
||||
return pkg.NewExec(
|
||||
"busybox-bin-"+version, nil, pkg.ExecTimeoutMax, false,
|
||||
"busybox-bin-"+version, arch, nil, pkg.ExecTimeoutMax, false, false,
|
||||
fhs.AbsRoot, []string{
|
||||
"PATH=/system/bin",
|
||||
},
|
||||
|
||||
+11
-9
@@ -122,6 +122,8 @@ type CMakeHelper struct {
|
||||
// Path elements joined with source.
|
||||
Append []string
|
||||
|
||||
// Value of CMAKE_BUILD_TYPE. The zero value is equivalent to "Release".
|
||||
BuildType string
|
||||
// CMake CACHE entries.
|
||||
Cache []KV
|
||||
// Runs after install.
|
||||
@@ -164,14 +166,7 @@ func (*CMakeHelper) wantsDir() string { return "/cure/" }
|
||||
// script generates the cure script.
|
||||
func (attr *CMakeHelper) script(name string) string {
|
||||
if attr == nil {
|
||||
attr = &CMakeHelper{
|
||||
Cache: []KV{
|
||||
{"CMAKE_BUILD_TYPE", "Release"},
|
||||
},
|
||||
}
|
||||
}
|
||||
if len(attr.Cache) == 0 {
|
||||
panic("CACHE must be non-empty")
|
||||
attr = new(CMakeHelper)
|
||||
}
|
||||
|
||||
generate := "Ninja"
|
||||
@@ -189,6 +184,13 @@ func (attr *CMakeHelper) script(name string) string {
|
||||
script += "\n" + test
|
||||
}
|
||||
|
||||
cache := make([]KV, 1, 1+len(attr.Cache))
|
||||
cache[0] = KV{"CMAKE_BUILD_TYPE", "Release"}
|
||||
if attr.BuildType != "" {
|
||||
cache[0][1] = attr.BuildType
|
||||
}
|
||||
cache = append(cache, attr.Cache...)
|
||||
|
||||
return `
|
||||
cmake -G ` + generate + ` \
|
||||
-DCMAKE_C_COMPILER_TARGET="${ROSA_TRIPLE}" \
|
||||
@@ -196,7 +198,7 @@ cmake -G ` + generate + ` \
|
||||
-DCMAKE_ASM_COMPILER_TARGET="${ROSA_TRIPLE}" \
|
||||
-DCMAKE_INSTALL_LIBDIR=lib \
|
||||
` + strings.Join(slices.Collect(func(yield func(string) bool) {
|
||||
for _, v := range attr.Cache {
|
||||
for _, v := range cache {
|
||||
if !yield("-D" + v[0] + "=" + v[1]) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newCurl() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "8.19.0"
|
||||
checksum = "YHuVLVVp8q_Y7-JWpID5ReNjq2Zk6t7ArHB6ngQXilp_R5l3cubdxu3UKo-xDByv"
|
||||
version = "8.20.0"
|
||||
checksum = "xyHXwrngIRGMasuzhn-I5MSCOhktwINbsWt1f_LuR-5jRVvyx_g6U1EQfDLEbr9r"
|
||||
)
|
||||
return t.NewPackage("curl", version, newTar(
|
||||
"https://curl.se/download/curl-"+version+".tar.bz2",
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newFontConfig() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "2.15.0"
|
||||
checksum = "rvlTL_zn-RwVkTBFdsOacUmEdLmwa3whTkuTzV8JH7kKbsFmnbG2qof3hN9ghr9d"
|
||||
)
|
||||
return t.NewPackage("fontconfig", version, newFromGitLab("gitlab.freedesktop.org", "fontconfig/fontconfig", version, checksum),
|
||||
nil,
|
||||
(*MesonHelper)(nil),
|
||||
Gperf,
|
||||
Freetype,
|
||||
Libexpat,
|
||||
Python,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[FontConfig] = Metadata{
|
||||
f: Toolchain.newFontConfig,
|
||||
Name: "fontconfig",
|
||||
Description: "freedesktop font discovery",
|
||||
Website: "https://fontconfig.pages.freedesktop.org/fontconfig/fontconfig-user.html",
|
||||
ID: 827,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newCairo() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.18.4"
|
||||
checksum = "_Ztw0ttmYLIiElqS27x2Iu4EneXOMNHp2or6bJA6suLeF3NRvORzUMPPQy9Wr5gT"
|
||||
)
|
||||
return t.NewPackage("cairo", version, newFromGitLab("gitlab.freedesktop.org", "cairo/cairo", version, checksum),
|
||||
nil,
|
||||
&MesonHelper{
|
||||
SkipTest: true,
|
||||
Setup: []KV{{"Dfreetype", "enabled"}, {"Dfontconfig", "enabled"}},
|
||||
},
|
||||
Automake,
|
||||
Libtool,
|
||||
Libpng,
|
||||
PkgConfig,
|
||||
FontConfig,
|
||||
Libexpat,
|
||||
Freetype,
|
||||
Pixman,
|
||||
Gawk,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Cairo] = Metadata{
|
||||
f: Toolchain.newCairo,
|
||||
Name: "cairo",
|
||||
Description: "freedesktop 2d rendering library",
|
||||
Website: "https://cairographics.org",
|
||||
ID: 247,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newHarfbuzz() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "14.2.0"
|
||||
checksum = "2VgjUmcPeIbleafZaGk5l7iGnag2qj0HTqrJ5770GzivebryZ2pbwVIha5j_24PH"
|
||||
)
|
||||
return t.NewPackage("harfbuzz", version, newFromGitHub("harfbuzz/harfbuzz", version, checksum),
|
||||
nil,
|
||||
(*MesonHelper)(nil),
|
||||
Freetype,
|
||||
GLib,
|
||||
Cairo,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[HarfBuzz] = Metadata{
|
||||
f: Toolchain.newHarfbuzz,
|
||||
Name: "harfbuzz",
|
||||
Description: "text shaping library",
|
||||
Website: "https://harfbuzz.github.io",
|
||||
ID: 1299,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newFribidi() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.0.16"
|
||||
checksum = "Sn9hwdgViXW9_MOi8ovAcBgQy6s6o4cc0o260k76nqCRzxmxxD1-B37GVkJ2EoMy"
|
||||
)
|
||||
return t.NewPackage("fribidi", version, newFromGitHub("fribidi/fribidi", "v"+version, checksum),
|
||||
nil,
|
||||
&MesonHelper{
|
||||
Setup: []KV{[2]string{"Ddocs", "false"}},
|
||||
},
|
||||
PkgConfig,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Fribidi] = Metadata{
|
||||
f: Toolchain.newFribidi,
|
||||
Name: "fribidi",
|
||||
Description: "bidirectional text library",
|
||||
Website: "https://www.gnu.org/software/fribidi/",
|
||||
ID: 857,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newLibdatrie() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "0.2.14"
|
||||
checksum = "JnJBEFrA8xYscXOSRJNGb1ZyRqrdsfP1_sdRIQeeSujdcsxmYT5Yl9m4byTsLozp"
|
||||
)
|
||||
return t.NewPackage("libdatrie", version, newFromGitHub("tlwg/libdatrie", "v"+version, checksum),
|
||||
nil,
|
||||
&MakeHelper{
|
||||
Generate: "sh autogen.sh",
|
||||
},
|
||||
Libtool,
|
||||
Automake,
|
||||
M4,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libdatrie] = Metadata{
|
||||
f: Toolchain.newLibdatrie,
|
||||
Name: "libdatrie",
|
||||
Description: "double-array trie library",
|
||||
Website: "https://linux.thai.net/~thep/datrie/datrie.html",
|
||||
ID: 7342,
|
||||
}
|
||||
}
|
||||
func (t Toolchain) newLibThai() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "0.1.30"
|
||||
checksum = "jQxUviF-lSiVrnqXebc4kROp0hrIeQbke2gvaeTfkCDp-wHkpxb3xP18fsNOF_F-"
|
||||
)
|
||||
return t.NewPackage("libthai", version, newFromGitHub("tlwg/libthai", "v"+version, checksum),
|
||||
nil,
|
||||
&MakeHelper{
|
||||
Generate: "sh autogen.sh",
|
||||
},
|
||||
Libtool,
|
||||
Automake,
|
||||
M4,
|
||||
Libdatrie,
|
||||
PkgConfig,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libthai] = Metadata{
|
||||
f: Toolchain.newLibThai,
|
||||
Name: "libthai",
|
||||
Description: "thai language handling library",
|
||||
Website: "https://linux.thai.net/",
|
||||
|
||||
ID: 1737,
|
||||
}
|
||||
}
|
||||
func (t Toolchain) newPango() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.56.3"
|
||||
checksum = "O73sc8axQyjIFpW4XkV7wi4HVSvmdUnx1uTn-HtbewXgrCVWX9Du-pssL5BK7rf1"
|
||||
)
|
||||
return t.NewPackage("pango", version, newFromGitLab("gitlab.gnome.org", "GNOME/pango", version, checksum),
|
||||
nil,
|
||||
&MesonHelper{
|
||||
SkipTest: true,
|
||||
},
|
||||
GLib,
|
||||
GObjectIntrospection,
|
||||
Freetype,
|
||||
PkgConfig,
|
||||
FontConfig,
|
||||
HarfBuzz,
|
||||
Fribidi,
|
||||
Cairo,
|
||||
Pixman,
|
||||
Libpng,
|
||||
Libexpat,
|
||||
Libdatrie,
|
||||
Libthai,
|
||||
Libtool,
|
||||
LibX11,
|
||||
LibXrender,
|
||||
LibXft,
|
||||
Diffutils,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Pango] = Metadata{
|
||||
f: Toolchain.newPango,
|
||||
Name: "pango",
|
||||
Description: "text rendering library",
|
||||
Website: "https://pango.org",
|
||||
ID: 11783,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newFreetype() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "2.14.3"
|
||||
checksum = "-WfLv8fVJNyCHpP_lriiDzOcVbBL9ajdQ3tl8AzIIUa9-8sVpU9irxOmSMgRHWYz"
|
||||
)
|
||||
return t.NewPackage("freetype", version, newTar(
|
||||
"https://download.savannah.gnu.org/releases/freetype/"+
|
||||
"freetype-"+version+".tar.gz",
|
||||
checksum,
|
||||
pkg.TarGzip,
|
||||
), nil, (*MakeHelper)(nil)), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Freetype] = Metadata{
|
||||
f: Toolchain.newFreetype,
|
||||
|
||||
Name: "freetype",
|
||||
Description: "a freely available software library to render fonts",
|
||||
Website: "http://www.freetype.org/",
|
||||
|
||||
ID: 854,
|
||||
}
|
||||
}
|
||||
@@ -55,6 +55,14 @@ disable_test t9300-fast-import
|
||||
disable_test t0211-trace2-perf
|
||||
disable_test t1517-outside-repo
|
||||
disable_test t2200-add-update
|
||||
disable_test t0027-auto-crlf
|
||||
disable_test t7513-interpret-trailers
|
||||
disable_test t7703-repack-geometric
|
||||
disable_test t7002-mv-sparse-checkout
|
||||
disable_test t1451-fsck-buffer
|
||||
disable_test t4104-apply-boundary
|
||||
disable_test t4200-rerere
|
||||
disable_test t5515-fetch-merge-logic
|
||||
`,
|
||||
Check: []string{
|
||||
"-C t",
|
||||
@@ -105,7 +113,7 @@ func (t Toolchain) NewViaGit(
|
||||
return t.New(strings.TrimSuffix(
|
||||
path.Base(url),
|
||||
".git",
|
||||
)+"-src-"+path.Base(rev), 0, t.AppendPresets(nil,
|
||||
)+"-src-"+path.Base(rev), THostNet, t.AppendPresets(nil,
|
||||
NSSCACert,
|
||||
Git,
|
||||
), &checksum, nil, `
|
||||
|
||||
+122
-8
@@ -17,10 +17,6 @@ func (t Toolchain) newSPIRVHeaders() (pkg.Artifact, string) {
|
||||
"vulkan-sdk-"+version,
|
||||
checksum,
|
||||
), nil, &CMakeHelper{
|
||||
Cache: []KV{
|
||||
{"CMAKE_BUILD_TYPE", "Release"},
|
||||
},
|
||||
|
||||
// upstream has no tests
|
||||
SkipTest: true,
|
||||
}), version
|
||||
@@ -67,7 +63,6 @@ func (t Toolchain) newSPIRVTools() (pkg.Artifact, string) {
|
||||
checksum,
|
||||
), nil, &CMakeHelper{
|
||||
Cache: []KV{
|
||||
{"CMAKE_BUILD_TYPE", "Release"},
|
||||
{"SPIRV-Headers_SOURCE_DIR", "/system"},
|
||||
},
|
||||
},
|
||||
@@ -96,8 +91,8 @@ func init() {
|
||||
|
||||
func (t Toolchain) newGlslang() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "16.2.0"
|
||||
checksum = "6_UuF9reLRDaVkgO-9IfB3kMwme3lQZM8LL8YsJwPdUFkrjzxJtf2A9X3w9nFxj2"
|
||||
version = "16.3.0"
|
||||
checksum = "xyqDf8k3-D0_BXHGi0uLgMglnJ05Rf3j73QgbDs3sGtKNdBIQhY8JfqX1NcNoJQN"
|
||||
)
|
||||
return t.NewPackage("glslang", version, newFromGitHub(
|
||||
"KhronosGroup/glslang",
|
||||
@@ -109,7 +104,6 @@ func (t Toolchain) newGlslang() (pkg.Artifact, string) {
|
||||
Chmod: true,
|
||||
}, &CMakeHelper{
|
||||
Cache: []KV{
|
||||
{"CMAKE_BUILD_TYPE", "Release"},
|
||||
{"BUILD_SHARED_LIBS", "ON"},
|
||||
{"ALLOW_EXTERNAL_SPIRV_TOOLS", "ON"},
|
||||
},
|
||||
@@ -132,3 +126,123 @@ func init() {
|
||||
ID: 205796,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newSPIRVLLVMTranslator() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "22.1.2"
|
||||
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(
|
||||
"KhronosGroup/SPIRV-LLVM-Translator",
|
||||
"v"+version, checksum,
|
||||
), &PackageAttr{
|
||||
Patches: []KV{
|
||||
{"remove-early-prefix", `diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index c000a77e..f18f3fde 100644
|
||||
--- a/CMakeLists.txt
|
||||
+++ b/CMakeLists.txt
|
||||
@@ -164,7 +164,7 @@ install(
|
||||
${LLVM_SPIRV_INCLUDE_DIRS}/LLVMSPIRVOpts.h
|
||||
${LLVM_SPIRV_INCLUDE_DIRS}/LLVMSPIRVExtensions.inc
|
||||
DESTINATION
|
||||
- ${CMAKE_INSTALL_PREFIX}/include/LLVMSPIRVLib
|
||||
+ include/LLVMSPIRVLib
|
||||
)
|
||||
|
||||
configure_file(LLVMSPIRVLib.pc.in ${CMAKE_BINARY_DIR}/LLVMSPIRVLib.pc @ONLY)
|
||||
@@ -172,5 +172,5 @@ install(
|
||||
FILES
|
||||
${CMAKE_BINARY_DIR}/LLVMSPIRVLib.pc
|
||||
DESTINATION
|
||||
- ${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}/pkgconfig
|
||||
+ lib${LLVM_LIBDIR_SUFFIX}/pkgconfig
|
||||
)
|
||||
;`},
|
||||
},
|
||||
|
||||
// litArgs emits shell syntax
|
||||
ScriptEarly: `
|
||||
export LIT_OPTS=` + litArgs(true, skipChecks...) + `
|
||||
`,
|
||||
}, &CMakeHelper{
|
||||
Cache: []KV{
|
||||
{"CMAKE_SKIP_BUILD_RPATH", "ON"},
|
||||
{"BUILD_SHARED_LIBS", "ON"},
|
||||
{"LLVM_SPIRV_ENABLE_LIBSPIRV_DIS", "ON"},
|
||||
{"LLVM_EXTERNAL_SPIRV_HEADERS_SOURCE_DIR", "/system"},
|
||||
{"LLVM_EXTERNAL_LIT", "/system/bin/lit"},
|
||||
{"LLVM_INCLUDE_TESTS", "ON"},
|
||||
},
|
||||
},
|
||||
Bash,
|
||||
LIT,
|
||||
|
||||
SPIRVTools,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[SPIRVLLVMTranslator] = Metadata{
|
||||
f: Toolchain.newSPIRVLLVMTranslator,
|
||||
|
||||
Name: "spirv-llvm-translator",
|
||||
Description: "bi-directional translation between SPIR-V and LLVM IR",
|
||||
Website: "https://github.com/KhronosGroup/SPIRV-LLVM-Translator",
|
||||
|
||||
Dependencies: P{
|
||||
SPIRVTools,
|
||||
},
|
||||
|
||||
ID: 227273,
|
||||
}
|
||||
}
|
||||
|
||||
+50
-6
@@ -1,11 +1,47 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// skipGNUTests generates a string for skipping specific tests by number in a
|
||||
// GNU test suite. This is nontrivial because the test suite does not support
|
||||
// excluding tests in any way, so ranges for all but the skipped tests have to
|
||||
// be specified instead.
|
||||
//
|
||||
// For example, to skip test 764, ranges around the skipped test must be
|
||||
// specified:
|
||||
//
|
||||
// 1-763 765-
|
||||
//
|
||||
// Tests are numbered starting from 1. The resulting string is unquoted.
|
||||
func skipGNUTests(tests ...int) string {
|
||||
tests = slices.Clone(tests)
|
||||
slices.Sort(tests)
|
||||
|
||||
var buf strings.Builder
|
||||
|
||||
if tests[0] != 1 {
|
||||
buf.WriteString("1-")
|
||||
}
|
||||
|
||||
for i, n := range tests {
|
||||
if n != 1 && (i == 0 || tests[i-1] != n-1) {
|
||||
buf.WriteString(strconv.Itoa(n - 1))
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
if i == len(tests)-1 || tests[i+1] != n+1 {
|
||||
buf.WriteString(strconv.Itoa(n + 1))
|
||||
buf.WriteString("-")
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (t Toolchain) newM4() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.4.21"
|
||||
@@ -47,7 +83,15 @@ func (t Toolchain) newBison() (pkg.Artifact, string) {
|
||||
"https://ftpmirror.gnu.org/gnu/bison/bison-"+version+".tar.gz",
|
||||
checksum,
|
||||
pkg.TarGzip,
|
||||
), nil, (*MakeHelper)(nil),
|
||||
), nil, &MakeHelper{
|
||||
Check: []string{
|
||||
"TESTSUITEFLAGS=" + jobsFlagE + "' " + skipGNUTests(
|
||||
// clang miscompiles (SIGILL)
|
||||
764,
|
||||
) + "'",
|
||||
"check",
|
||||
},
|
||||
},
|
||||
M4,
|
||||
Diffutils,
|
||||
Sed,
|
||||
@@ -798,7 +842,7 @@ func (t Toolchain) newGnuTLS() (pkg.Artifact, string) {
|
||||
)
|
||||
|
||||
var configureExtra []KV
|
||||
switch runtime.GOARCH {
|
||||
switch arch {
|
||||
case "arm64":
|
||||
configureExtra = []KV{
|
||||
{"disable-hardware-acceleration"},
|
||||
@@ -1098,12 +1142,12 @@ func init() {
|
||||
|
||||
func (t Toolchain) newGCC() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "15.2.0"
|
||||
checksum = "TXJ5WrbXlGLzy1swghQTr4qxgDCyIZFgJry51XEPTBZ8QYbVmFeB4lZbSMtPJ-a1"
|
||||
version = "16.1.0"
|
||||
checksum = "4ASoWbxaA2FW7PAB0zzHDPC5XnNhyaAyjtDPpGzceSLeYnEIXsNYZR3PA_Zu5P0K"
|
||||
)
|
||||
|
||||
var configureExtra []KV
|
||||
switch runtime.GOARCH {
|
||||
switch arch {
|
||||
case "amd64", "arm64":
|
||||
configureExtra = append(configureExtra, KV{"with-multilib-list", "''"})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSkipGNUTests(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := []struct {
|
||||
tests []int
|
||||
want string
|
||||
}{
|
||||
{[]int{764}, "1-763 765-"},
|
||||
{[]int{764, 0xcafe, 37, 9}, "1-8 10-36 38-763 765-51965 51967-"},
|
||||
{[]int{1, 2, 0xbed}, "3-3052 3054-"},
|
||||
{[]int{3, 4}, "1-2 5-"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(strings.Join(slices.Collect(func(yield func(string) bool) {
|
||||
for _, n := range tc.tests {
|
||||
yield(strconv.Itoa(n))
|
||||
}
|
||||
}), ","), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if got := skipGNUTests(tc.tests...); got != tc.want {
|
||||
t.Errorf("skipGNUTests: %q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+15
-12
@@ -1,7 +1,6 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"slices"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
@@ -35,6 +34,10 @@ func (t Toolchain) newGo(
|
||||
script string,
|
||||
extra ...pkg.Artifact,
|
||||
) pkg.Artifact {
|
||||
name := "all"
|
||||
if presetOpts&OptSkipCheck != 0 {
|
||||
name = "make"
|
||||
}
|
||||
return t.New("go"+version, 0, t.AppendPresets(extra,
|
||||
Bash,
|
||||
), nil, slices.Concat([]string{
|
||||
@@ -48,7 +51,7 @@ cp -r /usr/src/go /work/system
|
||||
cd /work/system/go/src
|
||||
chmod -R +w ..
|
||||
`+script+`
|
||||
./all.bash
|
||||
./`+name+`.bash
|
||||
|
||||
mkdir /work/system/bin
|
||||
ln -s \
|
||||
@@ -69,7 +72,7 @@ func (t Toolchain) newGoLatest() (pkg.Artifact, string) {
|
||||
|
||||
finalEnv []string
|
||||
)
|
||||
switch runtime.GOARCH {
|
||||
switch arch {
|
||||
case "amd64":
|
||||
bootstrapExtra = append(bootstrapExtra, t.newGoBootstrap())
|
||||
|
||||
@@ -79,7 +82,7 @@ func (t Toolchain) newGoLatest() (pkg.Artifact, string) {
|
||||
finalEnv = append(finalEnv, "CGO_ENABLED=0")
|
||||
|
||||
default:
|
||||
panic("unsupported target " + runtime.GOARCH)
|
||||
panic("unsupported target " + arch)
|
||||
}
|
||||
|
||||
go119 := t.newGo(
|
||||
@@ -104,7 +107,7 @@ echo \
|
||||
[]string{"CGO_ENABLED=0"}, `
|
||||
sed -i \
|
||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
||||
cmd/link/internal/`+arch+`/obj.go
|
||||
|
||||
rm \
|
||||
crypto/tls/handshake_client_test.go \
|
||||
@@ -122,17 +125,17 @@ echo \
|
||||
[]string{"CGO_ENABLED=0"}, `
|
||||
sed -i \
|
||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
||||
cmd/link/internal/`+arch+`/obj.go
|
||||
`, go121,
|
||||
)
|
||||
|
||||
go125 := t.newGo(
|
||||
"1.25.9",
|
||||
"gShJb9uOMk5AxqPSwvn53ZO56S6PyP6nfojzrHUiJ3krAvrgjJpYa6-DPA-jxbpN",
|
||||
"1.25.10",
|
||||
"TwKwatkpwal-j9U2sDSRPEdM3YesI4Gm88YgGV59wtU-L85K9gA7UPy9SCxn6PMb",
|
||||
[]string{"CGO_ENABLED=0"}, `
|
||||
sed -i \
|
||||
's,/lib/ld-musl-`+linuxArch()+`.so.1,/system/bin/linker,' \
|
||||
cmd/link/internal/`+runtime.GOARCH+`/obj.go
|
||||
cmd/link/internal/`+arch+`/obj.go
|
||||
|
||||
rm \
|
||||
os/root_unix_test.go \
|
||||
@@ -141,8 +144,8 @@ rm \
|
||||
)
|
||||
|
||||
const (
|
||||
version = "1.26.2"
|
||||
checksum = "v-6BE89_1g3xYf-9oIYpJKFXlo3xKHYJj2_VGkaUq8ZVkIVQmLwrto-xGG03OISH"
|
||||
version = "1.26.3"
|
||||
checksum = "lEiFocZFnN5fKvZzmwVdqc9pYUjAuhzqZGbuiOqxUP4XdcY8yECisKcqsQ_eNn1N"
|
||||
)
|
||||
return t.newGo(
|
||||
version,
|
||||
@@ -150,7 +153,7 @@ rm \
|
||||
finalEnv, `
|
||||
sed -i \
|
||||
'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 \
|
||||
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
|
||||
internal/runtime/gc/scan/scan_amd64.go
|
||||
|
||||
+90
-2
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
func (t Toolchain) newGLib() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "2.88.0"
|
||||
checksum = "T79Cg4z6j-sDZ2yIwvbY4ccRv2-fbwbqgcw59F5NQ6qJT6z4v261vbYp3dHO6Ma3"
|
||||
version = "2.88.1"
|
||||
checksum = "Rkszn6W4RHjyspyqfXdVAVawdwDJCuS0Zu0f7qot7tbJhnw2fUDoUUJB40m-1MCX"
|
||||
)
|
||||
return t.NewPackage("glib", version, t.newTagRemote(
|
||||
"https://gitlab.gnome.org/GNOME/glib.git",
|
||||
@@ -58,3 +58,91 @@ func init() {
|
||||
ID: 10024,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newSharedMimeInfo() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "2.4"
|
||||
checksum = "RrqfOT1gGuYfN7pkwgeLPfljkk9eL5BwRsNTuJNZjOk1vT5frTJ1EshDvaSqKwiL"
|
||||
)
|
||||
return t.NewPackage("shared-mime-info", version, newFromGitLab("gitlab.freedesktop.org", "xdg/shared-mime-info", version, checksum),
|
||||
nil,
|
||||
&MesonHelper{
|
||||
SkipTest: true,
|
||||
},
|
||||
GLib,
|
||||
Meson,
|
||||
Libxml2,
|
||||
Gettext,
|
||||
Libpng,
|
||||
Libjpeg,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[SharedMimeInfo] = Metadata{
|
||||
f: Toolchain.newSharedMimeInfo,
|
||||
Name: "shared-mime-info",
|
||||
Description: "xdg MIME repository",
|
||||
Website: "https://www.freedesktop.org/wiki/Software/shared-mime-info/",
|
||||
ID: 5524,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newGobjectIntrospection() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.86.0"
|
||||
checksum = "M-eq1kEEX215x0VMfTIfJXNfVnZwiC41QK33tlCeK0_GDwGUILQm0SqvRSySNoka"
|
||||
)
|
||||
return t.NewPackage("gobject-introspection", version, t.NewViaGit("https://gitlab.gnome.org/GNOME/gobject-introspection.git", version, mustDecode(checksum)),
|
||||
&PackageAttr{
|
||||
Writable: true,
|
||||
},
|
||||
&MesonHelper{
|
||||
SkipTest: true,
|
||||
},
|
||||
GLib,
|
||||
PythonSetuptools,
|
||||
Flex,
|
||||
Bison,
|
||||
Cairo,
|
||||
Libffi,
|
||||
Libpng,
|
||||
M4,
|
||||
PythonMako,
|
||||
Pixman,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[GObjectIntrospection] = Metadata{
|
||||
f: Toolchain.newGobjectIntrospection,
|
||||
Name: "gobject-introspection",
|
||||
Description: "introspection toolkit",
|
||||
Website: "https://gi.readthedocs.io/",
|
||||
ID: 1223,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newGconf() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "3.2.6"
|
||||
checksum = "oSoPCk8J4Xd1gF1RLPqzdvHQfEn2eOE9qpLoT2CyDwnkUERgg2990dHN_AKhVgJp"
|
||||
)
|
||||
return t.NewPackage("gconf", version, newFromGitLab("gitlab.gnome.org", "Archive/gconf", version, checksum),
|
||||
nil,
|
||||
&MakeHelper{
|
||||
Configure: []KV{{"disable-orbit"}, {"disable-introspection"}},
|
||||
Generate: "autoreconf -if",
|
||||
},
|
||||
Automake,
|
||||
Libtool,
|
||||
GLib,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Gconf] = Metadata{
|
||||
f: Toolchain.newGconf,
|
||||
Name: "gconf",
|
||||
Description: "outdated configuration database library",
|
||||
Website: "http://projects.gnome.org/gconf/",
|
||||
ID: 8423,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ func (t Toolchain) newHakurei(
|
||||
withHostname bool,
|
||||
) pkg.Artifact {
|
||||
hostname := `
|
||||
echo '# Building test helper (hostname).'
|
||||
go build -v -o /bin/hostname /usr/src/hostname/main.go
|
||||
echo
|
||||
echo 'Building test helper (hostname).'
|
||||
go build -o /bin/hostname /usr/src/hostname/main.go
|
||||
`
|
||||
if !withHostname {
|
||||
hostname = ""
|
||||
@@ -64,9 +63,9 @@ func init() {
|
||||
return t.newHakurei("", `
|
||||
mkdir -p /work/system/libexec/hakurei/
|
||||
|
||||
echo '# Building hakurei.'
|
||||
go generate -v ./...
|
||||
go build -trimpath -v -tags=rosa -o /work/system/libexec/hakurei -ldflags="-s -w
|
||||
echo "Building hakurei for $(go env GOOS)/$(go env GOARCH)."
|
||||
go generate ./...
|
||||
go build -trimpath -tags=rosa -o /work/system/libexec/hakurei -ldflags="-s -w
|
||||
-buildid=
|
||||
-linkmode external
|
||||
-extldflags=-static
|
||||
@@ -77,7 +76,7 @@ go build -trimpath -v -tags=rosa -o /work/system/libexec/hakurei -ldflags="-s -w
|
||||
" ./...
|
||||
echo
|
||||
|
||||
echo '# Testing hakurei.'
|
||||
echo '##### Testing hakurei.'
|
||||
go test -ldflags='-buildid= -linkmode external -extldflags=-static' ./...
|
||||
echo
|
||||
|
||||
@@ -97,9 +96,13 @@ mkdir -p /work/system/bin/
|
||||
}
|
||||
artifactsM[HakureiDist] = Metadata{
|
||||
f: func(t Toolchain) (pkg.Artifact, string) {
|
||||
name := "all"
|
||||
if presetOpts&OptSkipCheck != 0 {
|
||||
name = "make"
|
||||
}
|
||||
return t.newHakurei("-dist", `
|
||||
export HAKUREI_VERSION
|
||||
DESTDIR=/work /usr/src/hakurei/all.sh
|
||||
DESTDIR=/work /usr/src/hakurei/`+name+`.sh
|
||||
`, true), hakureiVersion
|
||||
},
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
const hakureiVersion = "0.4.0"
|
||||
const hakureiVersion = "0.4.2"
|
||||
|
||||
// hakureiSource is the source code of a hakurei release.
|
||||
var hakureiSource = newTar(
|
||||
"https://git.gensokyo.uk/rosa/hakurei/archive/"+
|
||||
"v"+hakureiVersion+".tar.gz",
|
||||
"wfQ9DqCW0Fw9o91wj-I55waoqzB-UqzzuC0_2h-P-1M78SgZ1WHSPCDJMth6EyC2",
|
||||
"jadgaOrxv5ABGvzQ_Rk0aPGz7U8K-427TbMhQNQ32scSizEnlR44Pu7NoWYWVZWq",
|
||||
pkg.TarGzip,
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newJikes() pkg.Artifact {
|
||||
const (
|
||||
version = "1.22"
|
||||
checksum = "qb5Pf6P7RBrXRVLNEwfGxqhb05WW-MjJyJw7O-fYKizNGDNJEpsttbmOsFge0MO1"
|
||||
)
|
||||
return t.NewPackage("jikes", version,
|
||||
newTar("https://sourceforge.net/projects/jikes/files/Jikes/"+version+"/jikes-"+version+".tar.bz2/download",
|
||||
checksum, pkg.TarBzip2),
|
||||
&PackageAttr{},
|
||||
&MakeHelper{
|
||||
Generate: "autoreconf -if",
|
||||
Make: []string{
|
||||
"CXXFLAGS='-std=c++03'",
|
||||
},
|
||||
},
|
||||
Automake,
|
||||
)
|
||||
}
|
||||
|
||||
func (t Toolchain) newClasspath(version, checksum string, extra ...pkg.Artifact) pkg.Artifact {
|
||||
attr := &PackageAttr{
|
||||
Patches: []KV{{"classpath0.93-include-stdlib",
|
||||
`diff --git a/native/fdlibm/dtoa.c b/native/fdlibm/dtoa.c
|
||||
index 5fb6aae..e710d96 100644
|
||||
--- a/native/fdlibm/dtoa.c
|
||||
+++ b/native/fdlibm/dtoa.c
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "mprec.h"
|
||||
#include <string.h>
|
||||
+#include <stdlib.h>
|
||||
|
||||
static int
|
||||
_DEFUN (quorem,
|
||||
`}},
|
||||
}
|
||||
helper := &MakeHelper{
|
||||
SkipCheck: true,
|
||||
Generate: "autoreconf -if",
|
||||
Configure: []KV{{"with-jikes"}, {"disable-gtk-peer"}, {"disable-gconf-peer"}, {"disable-Werror"}, {"disable-plugin"}},
|
||||
}
|
||||
script := attr.ScriptEarly
|
||||
script += helper.scriptEarly()
|
||||
|
||||
source := newTar(
|
||||
"https://ftp.gnu.org/gnu/classpath/classpath-"+version+".tar.gz",
|
||||
checksum,
|
||||
pkg.TarGzip,
|
||||
)
|
||||
|
||||
wantsChmod, wantsWrite := helper.wantsChmod(), helper.wantsWrite()
|
||||
pExtra := []PArtifact{Automake, Libtool, LibX11, LibXtst, LibXi, PkgConfig, Gzip, GLib, Findutils, Unzip}
|
||||
extraRes := make([]pkg.Artifact, 0, 1<<3+len(pExtra))
|
||||
{
|
||||
pv := paGet()
|
||||
for _, p := range helper.extra(attr.Flag) {
|
||||
extraRes = t.appendPreset(extraRes, pv, p)
|
||||
}
|
||||
for _, p := range pExtra {
|
||||
extraRes = t.appendPreset(extraRes, pv, p)
|
||||
}
|
||||
paPut(pv)
|
||||
}
|
||||
var (
|
||||
scriptEarly string
|
||||
sourceSuffix string
|
||||
)
|
||||
|
||||
if _, ok := source.(pkg.FileArtifact); ok {
|
||||
if attr.Writable || attr.Chmod ||
|
||||
wantsChmod || wantsWrite ||
|
||||
len(attr.Patches) > 0 {
|
||||
panic("source processing requested on a xz-compressed tarball")
|
||||
}
|
||||
|
||||
sourceSuffix = ".tar.xz"
|
||||
scriptEarly += `
|
||||
tar -C /usr/src/ -xf '/usr/src/` + "classpath" + `.tar.xz'
|
||||
mv '/usr/src/` + "classpath" + `-` + version + `' '/usr/src/` + "classpath" + `'
|
||||
`
|
||||
}
|
||||
|
||||
dir := helper.wantsDir()
|
||||
helperScriptEarly := helper.scriptEarly()
|
||||
if attr.EnterSource ||
|
||||
dir == "" ||
|
||||
attr.ScriptEarly != "" ||
|
||||
helperScriptEarly != "" {
|
||||
scriptEarly += `
|
||||
cd '/usr/src/` + "classpath" + `/'
|
||||
`
|
||||
}
|
||||
scriptEarly += attr.ScriptEarly + helperScriptEarly
|
||||
if dir != "" && dir != helperInPlace {
|
||||
if helper.createDir() {
|
||||
scriptEarly += "\nmkdir -p " + dir
|
||||
}
|
||||
scriptEarly += "\ncd " + dir + "\n"
|
||||
} else if !attr.EnterSource && attr.ScriptEarly == "" {
|
||||
panic("cannot remain in root")
|
||||
}
|
||||
|
||||
return t.New(
|
||||
"classpath"+"-"+version,
|
||||
attr.Flag,
|
||||
slices.Concat(extra, extraRes),
|
||||
attr.KnownChecksum,
|
||||
attr.Env,
|
||||
scriptEarly+helper.script("classpath"),
|
||||
slices.Concat(attr.Paths, []pkg.ExecPath{
|
||||
pkg.Path(AbsUsrSrc.Append(
|
||||
"classpath"+sourceSuffix,
|
||||
), attr.Writable || wantsWrite, t.NewPatchedSource(
|
||||
"classpath", version, source, !attr.Chmod && !wantsChmod, attr.Patches...,
|
||||
)),
|
||||
})...,
|
||||
)
|
||||
}
|
||||
func (t Toolchain) newLatestJdk() (pkg.Artifact, string) {
|
||||
jikes := t.newJikes()
|
||||
classpath093 := t.newClasspath("0.93", "WKsqDegi4rVCdQ2uJc3BOPuMnovU_cbiQy7nAzvYYtYnw75V6--sZFyRKnfiwGg9", jikes)
|
||||
return classpath093, "0.93"
|
||||
}
|
||||
func init() {
|
||||
artifactsM[JDK] = Metadata{
|
||||
f: Toolchain.newLatestJdk,
|
||||
Name: "jdk",
|
||||
Description: "java toolchain",
|
||||
Website: "https://jikes.sourceforge.net/",
|
||||
}
|
||||
}
|
||||
+22
-4
@@ -2,12 +2,12 @@ package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
const kernelVersion = "6.12.84"
|
||||
const kernelVersion = "6.12.87"
|
||||
|
||||
var kernelSource = newTar(
|
||||
"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/"+
|
||||
"snapshot/linux-"+kernelVersion+".tar.gz",
|
||||
"GJLUEu68r3DpLYoTcMl4wA_ThMBs_Zwc0gZsp82ii_3AOfcVxpI639IKfq2jAAY2",
|
||||
"QTl5teIy0K5KsOLYGHQ3FbnPCZNRH2bySXVzghiOoHDdM3zAcSPUkmdly85lMzHx",
|
||||
pkg.TarGzip,
|
||||
)
|
||||
|
||||
@@ -29,8 +29,22 @@ func init() {
|
||||
}
|
||||
|
||||
func (t Toolchain) newKernelHeaders() (pkg.Artifact, string) {
|
||||
const checksum = "lCmBNcMeUmXifg0vecKOPy3GAaFcJSmOPnf3wit9xYTDSTsFADPt1xxUFfmTn1fD"
|
||||
return t.NewPackage("kernel-headers", kernelVersion, kernelSource, &PackageAttr{
|
||||
Flag: TEarly,
|
||||
Flag: TEarly,
|
||||
KnownChecksum: new(mustDecode(checksum)),
|
||||
|
||||
Paths: []pkg.ExecPath{
|
||||
// updated manually for API changes
|
||||
pkg.Path(AbsUsrSrc.Append("version.h"), false, pkg.NewFile(
|
||||
"version.h", []byte(`#define LINUX_VERSION_CODE 396372
|
||||
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
|
||||
#define LINUX_VERSION_MAJOR 6
|
||||
#define LINUX_VERSION_PATCHLEVEL 12
|
||||
#define LINUX_VERSION_SUBLEVEL 84
|
||||
`),
|
||||
)),
|
||||
},
|
||||
}, &MakeHelper{
|
||||
SkipConfigure: true,
|
||||
|
||||
@@ -43,7 +57,11 @@ func (t Toolchain) newKernelHeaders() (pkg.Artifact, string) {
|
||||
"INSTALL_HDR_PATH=/work/system",
|
||||
"headers_install",
|
||||
},
|
||||
Install: "# headers installed during make",
|
||||
Install: `
|
||||
cat \
|
||||
/usr/src/version.h > \
|
||||
/work/system/include/linux/version.h
|
||||
`,
|
||||
},
|
||||
Rsync,
|
||||
), kernelVersion
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newLibmd() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.1.0"
|
||||
checksum = "9apYqPPZm0j5HQT8sCsVIhnVIqRD7XgN7kPIaTwTqnTuUq5waUAMq4M7ev8CODJ1"
|
||||
version = "1.2.0"
|
||||
checksum = "1rJ6joAO0wwMZvSfnRNkc1MOhywyAq7SM8VmF92NvDtv7Qdl1LRbjm5fg_DFFtGj"
|
||||
)
|
||||
return t.NewPackage("libmd", version, t.newTagRemote(
|
||||
"https://git.hadrons.org/git/libmd.git",
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newLibepoxy() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.5.10"
|
||||
checksum = "OHI8wshrlGw6BMGrmSyejJtwzM2gPhyFJrTsKxULyKMmYrfgcOe7Iw2ibVoUND_Q"
|
||||
)
|
||||
return t.NewPackage("libepoxy", version, newFromGitHub(
|
||||
"anholt/libepoxy",
|
||||
version,
|
||||
checksum,
|
||||
), nil, &MesonHelper{
|
||||
Setup: []KV{
|
||||
{"Dglx", "no"},
|
||||
{"Degl", "no"},
|
||||
},
|
||||
},
|
||||
LibX11,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libepoxy] = Metadata{
|
||||
f: Toolchain.newLibepoxy,
|
||||
|
||||
Name: "libepoxy",
|
||||
Description: "a library for handling OpenGL function pointer management",
|
||||
Website: "https://github.com/anholt/libepoxy",
|
||||
|
||||
ID: 6090,
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
func (t Toolchain) newLibexpat() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "2.8.0"
|
||||
checksum = "pnwZ_JSif-OfoWIwk2JYXWHagOWMA3Sh-Ea0p-4Rz9U9mDEeAebhyvnfD7OYOMCk"
|
||||
version = "2.8.1"
|
||||
checksum = "iMEtbOJhQfGof2GxSlxffQSI1va_NDDQ9VIuqcPbNZ0291Dr8wttD5QecYyjIQap"
|
||||
)
|
||||
return t.NewPackage("libexpat", version, newFromGitHubRelease(
|
||||
"libexpat/libexpat",
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newLibjpeg() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "10"
|
||||
checksum = "N-o4pq6wO8fPeIePdi6-fZtiiRUzdizja_B-BYpmx-GU2Qesdgg0QF0ub-jtiYmd"
|
||||
)
|
||||
return t.NewPackage("libjpeg", version, newTar("https://www.ijg.org/files/jpegsrc.v"+version+".tar.gz", checksum, pkg.TarGzip),
|
||||
nil,
|
||||
(*MakeHelper)(nil),
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libjpeg] = Metadata{
|
||||
f: Toolchain.newLibjpeg,
|
||||
|
||||
Name: "libjpeg",
|
||||
Description: "jpeg library",
|
||||
Website: "https://www.ijg.org",
|
||||
ID: 21362,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newLibpng() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.6.58"
|
||||
checksum = "m_a5lROJH7vmF3cMjqwTUqURuQLhV1JQx2ySPzcN3VPdgDB9pG3UINsIx_mtkr-t"
|
||||
)
|
||||
return t.NewPackage("libpng", version, newTar(
|
||||
"https://downloads.sourceforge.net/project/libpng/libpng"+
|
||||
strings.Join(strings.SplitN(version, ".", 3)[:2], "")+
|
||||
"/"+version+"/libpng-"+version+".tar.gz",
|
||||
checksum,
|
||||
pkg.TarGzip,
|
||||
), nil, (*MakeHelper)(nil),
|
||||
Zlib,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libpng] = Metadata{
|
||||
f: Toolchain.newLibpng,
|
||||
|
||||
Name: "libpng",
|
||||
Description: "the official PNG reference library",
|
||||
Website: "https://www.libpng.org/pub/png/libpng.html",
|
||||
|
||||
Dependencies: P{
|
||||
Zlib,
|
||||
},
|
||||
|
||||
ID: 1705,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newLibtirpc() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.3.7"
|
||||
checksum = "nzFfu7LNvnSNiNAryD1vtnNWnU-Xqee8KqfXUKoBf5yjb5-dkeRkYuRijdCoYLof"
|
||||
)
|
||||
return t.NewPackage("libtirpc", version, t.newTagRemote(
|
||||
"git://linux-nfs.org/~steved/libtirpc",
|
||||
"libtirpc-"+
|
||||
strings.Join(strings.SplitN(version, ".", 3), "-"),
|
||||
checksum,
|
||||
), nil, &MakeHelper{
|
||||
Generate: "sh -e ./bootstrap",
|
||||
Configure: []KV{
|
||||
{"CFLAGS", `"$(pkg-config --cflags libbsd-overlay) ${CFLAGS:-}"`},
|
||||
{"disable-gssapi"},
|
||||
},
|
||||
},
|
||||
Automake,
|
||||
Libtool,
|
||||
PkgConfig,
|
||||
|
||||
Libbsd,
|
||||
KernelHeaders,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libtirpc] = Metadata{
|
||||
f: Toolchain.newLibtirpc,
|
||||
|
||||
Name: "libtirpc",
|
||||
Description: "a port of Suns Transport-Independent RPC library to Linux",
|
||||
Website: "https://sourceforge.net/projects/libtirpc/",
|
||||
|
||||
ID: 1740,
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newLibucontext() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.5"
|
||||
checksum = "Ggk7FMmDNBdCx1Z9PcNWWW6LSpjGYssn2vU0GK5BLXJYw7ZxZbA2m_eSgT9TFnIG"
|
||||
version = "1.5.1"
|
||||
checksum = "mUgeyJknjMxT-5fORzz-rqhZfP3Y7EZGBhOwvhuX7MsF4Pk9wkuwtrLf5IML-jWu"
|
||||
)
|
||||
return t.NewPackage("libucontext", version, newFromGitHub(
|
||||
"kaniini/libucontext",
|
||||
|
||||
+196
-23
@@ -2,20 +2,173 @@ package rosa
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
// litArgs returns [LIT] arguments for optional verbosity and check skipping.
|
||||
func litArgs(verbose bool, skipChecks ...string) string {
|
||||
args := []string{"-sv"}
|
||||
if verbose {
|
||||
args[0] = "--verbose"
|
||||
}
|
||||
|
||||
if len(skipChecks) > 0 {
|
||||
skipChecks = slices.Clone(skipChecks)
|
||||
for i, s := range skipChecks {
|
||||
s = regexp.QuoteMeta(s)
|
||||
s = strings.ReplaceAll(s, "/", "\\/")
|
||||
skipChecks[i] = s
|
||||
}
|
||||
args = append(args,
|
||||
"--filter-out='\\''"+strings.Join(skipChecks, "|")+"'\\''")
|
||||
}
|
||||
|
||||
return "'" + strings.Join(args, " ") + "'"
|
||||
}
|
||||
|
||||
func (t Toolchain) newEarlyCompilerRT() (pkg.Artifact, string) {
|
||||
version := t.Version(llvmSource)
|
||||
major, _, _ := strings.Cut(version, ".")
|
||||
return t.NewPackage("early-compiler-rt", version, t.Load(llvmSource), &PackageAttr{
|
||||
Flag: TExclusive,
|
||||
}, &CMakeHelper{
|
||||
Append: []string{"compiler-rt"},
|
||||
|
||||
Cache: []KV{
|
||||
{"ENABLE_LINKER_BUILD_ID", "ON"},
|
||||
|
||||
// libc++ not yet available
|
||||
{"CMAKE_CXX_COMPILER_TARGET", ""},
|
||||
|
||||
{"LLVM_HOST_TRIPLE", `"${ROSA_TRIPLE}"`},
|
||||
{"LLVM_DEFAULT_TARGET_TRIPLE", `"${ROSA_TRIPLE}"`},
|
||||
{"LLVM_ENABLE_RTTI", "ON"},
|
||||
{"LLVM_BUILD_LLVM_DYLIB", "ON"},
|
||||
{"LLVM_LINK_LLVM_DYLIB", "ON"},
|
||||
{"LLVM_ENABLE_PER_TARGET_RUNTIME_DIR", "ON"},
|
||||
|
||||
{"COMPILER_RT_BUILD_BUILTINS", "ON"},
|
||||
{"COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"},
|
||||
{"COMPILER_RT_USE_BUILTINS_LIBRARY", "ON"},
|
||||
{"COMPILER_RT_SANITIZERS_TO_BUILD", "asan"},
|
||||
{"COMPILER_RT_BUILD_GWP_ASAN", "OFF"},
|
||||
|
||||
// does not work without libunwind
|
||||
{"COMPILER_RT_BUILD_CTX_PROFILE", "OFF"},
|
||||
{"COMPILER_RT_BUILD_LIBFUZZER", "OFF"},
|
||||
{"COMPILER_RT_BUILD_MEMPROF", "OFF"},
|
||||
{"COMPILER_RT_BUILD_PROFILE", "OFF"},
|
||||
{"COMPILER_RT_BUILD_XRAY", "OFF"},
|
||||
},
|
||||
SkipTest: true,
|
||||
Script: `
|
||||
mkdir -p "/work/system/lib/clang/` + major + `/lib/"
|
||||
ln -s \
|
||||
"../../../${ROSA_TRIPLE}" \
|
||||
"/work/system/lib/clang/` + major + `/lib/"
|
||||
|
||||
ln -s \
|
||||
"clang_rt.crtbegin-` + linuxArch() + `.o" \
|
||||
"/work/system/lib/${ROSA_TRIPLE}/crtbeginS.o"
|
||||
ln -s \
|
||||
"clang_rt.crtend-` + linuxArch() + `.o" \
|
||||
"/work/system/lib/${ROSA_TRIPLE}/crtendS.o"
|
||||
`,
|
||||
},
|
||||
Python,
|
||||
|
||||
muslHeaders,
|
||||
KernelHeaders,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[earlyCompilerRT] = Metadata{
|
||||
f: Toolchain.newEarlyCompilerRT,
|
||||
|
||||
Name: "early-compiler-rt",
|
||||
Description: "early LLVM runtime: compiler-rt",
|
||||
|
||||
Dependencies: P{
|
||||
Musl,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newEarlyRuntimes() (pkg.Artifact, string) {
|
||||
version := t.Version(llvmSource)
|
||||
return t.NewPackage("early-runtimes", version, t.Load(llvmSource), &PackageAttr{
|
||||
Flag: TExclusive,
|
||||
}, &CMakeHelper{
|
||||
Append: []string{"runtimes"},
|
||||
|
||||
Cache: []KV{
|
||||
{"ENABLE_LINKER_BUILD_ID", "ON"},
|
||||
|
||||
// libc++ not yet available
|
||||
{"CMAKE_CXX_COMPILER_WORKS", "ON"},
|
||||
|
||||
{"LLVM_HOST_TRIPLE", `"${ROSA_TRIPLE}"`},
|
||||
{"LLVM_DEFAULT_TARGET_TRIPLE", `"${ROSA_TRIPLE}"`},
|
||||
{"LLVM_ENABLE_RTTI", "ON"},
|
||||
{"LLVM_BUILD_LLVM_DYLIB", "ON"},
|
||||
{"LLVM_LINK_LLVM_DYLIB", "ON"},
|
||||
{"LLVM_ENABLE_RUNTIMES", "'libunwind;libcxx;libcxxabi'"},
|
||||
|
||||
{"LIBUNWIND_ENABLE_ASSERTIONS", "OFF"},
|
||||
{"LIBUNWIND_USE_COMPILER_RT", "ON"},
|
||||
{"LIBCXX_HAS_MUSL_LIBC", "ON"},
|
||||
{"LIBCXX_USE_COMPILER_RT", "ON"},
|
||||
{"LIBCXX_CXX_ABI", "libcxxabi"},
|
||||
{"LIBCXX_ENABLE_STATIC_ABI_LIBRARY", "OFF"},
|
||||
{"LIBCXX_HARDENING_MODE", "fast"},
|
||||
{"LIBCXX_HAS_ATOMIC_LIB", "OFF"},
|
||||
{"LIBCXXABI_USE_COMPILER_RT", "ON"},
|
||||
{"LIBCXXABI_USE_LLVM_UNWINDER", "ON"},
|
||||
{"LIBCXXABI_ENABLE_STATIC_UNWINDER", "OFF"},
|
||||
{"LIBCXXABI_HAS_CXA_THREAD_ATEXIT_IMPL", "OFF"},
|
||||
|
||||
{"LLVM_ENABLE_ZLIB", "FORCE_ON"},
|
||||
{"LLVM_ENABLE_ZSTD", "FORCE_ON"},
|
||||
{"LLVM_ENABLE_LIBXML2", "OFF"},
|
||||
},
|
||||
SkipTest: true,
|
||||
},
|
||||
Python,
|
||||
|
||||
Zlib,
|
||||
Zstd,
|
||||
earlyCompilerRT,
|
||||
KernelHeaders,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[earlyRuntimes] = Metadata{
|
||||
f: Toolchain.newEarlyRuntimes,
|
||||
|
||||
Name: "early-runtimes",
|
||||
Description: "early LLVM runtimes: libunwind, libcxx, libcxxabi",
|
||||
|
||||
Dependencies: P{
|
||||
earlyCompilerRT,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newLLVM() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "22.1.4"
|
||||
checksum = "Bk3t-tV5sD5T0bqefFMcLeFuAwXnhFipywZmqst5hAZs97QQWGKB_5XyAFjj5tDB"
|
||||
)
|
||||
var early PArtifact = muslHeaders
|
||||
if t.isStage0() {
|
||||
// The LLVM build system uses the system installation when building with
|
||||
// LLVM_LINK_LLVM_DYLIB, since it builds runtimes after the fact, using
|
||||
// the just-built toolchain. This is unacceptable in stage0 due to the
|
||||
// potential version difference. Later stages bootstrap off of runtimes
|
||||
// of its previous stage via 3-stage determinism.
|
||||
early = earlyRuntimes
|
||||
}
|
||||
|
||||
cache := []KV{
|
||||
{"CMAKE_BUILD_TYPE", "Release"},
|
||||
|
||||
{"ENABLE_LINKER_BUILD_ID", "ON"},
|
||||
{"COMPILER_RT_USE_BUILTINS_LIBRARY", "ON"},
|
||||
{"COMPILER_RT_DEFAULT_TARGET_ONLY", "ON"},
|
||||
@@ -37,6 +190,7 @@ func (t Toolchain) newLLVM() (pkg.Artifact, string) {
|
||||
{"LLVM_ENABLE_ZLIB", "FORCE_ON"},
|
||||
{"LLVM_ENABLE_ZSTD", "FORCE_ON"},
|
||||
{"LLVM_ENABLE_PER_TARGET_RUNTIME_DIR", "ON"},
|
||||
{"LLVM_INCLUDE_BENCHMARKS", "OFF"},
|
||||
{"CLANG_DEFAULT_RTLIB", "compiler-rt"},
|
||||
{"CLANG_DEFAULT_UNWINDLIB", "libunwind"},
|
||||
{"CLANG_DEFAULT_CXX_STDLIB", "libc++"},
|
||||
@@ -93,10 +247,14 @@ func (t Toolchain) newLLVM() (pkg.Artifact, string) {
|
||||
// unwind: fails on musl
|
||||
"eh_frame_fde_pc_range",
|
||||
}
|
||||
for i, s := range skipChecks {
|
||||
s = regexp.QuoteMeta(s)
|
||||
s = strings.ReplaceAll(s, "/", "\\/")
|
||||
skipChecks[i] = s
|
||||
switch arch {
|
||||
case "arm64":
|
||||
skipChecks = append(skipChecks,
|
||||
// LLVM: intermittently crashes
|
||||
"ExecutionEngine/OrcLazy/multiple-compile-threads-basic.ll",
|
||||
// unwind: unexpectedly passes
|
||||
"unwind_leaffunction",
|
||||
)
|
||||
}
|
||||
|
||||
if presetOpts&OptLLVMNoLTO == 0 {
|
||||
@@ -110,25 +268,22 @@ func (t Toolchain) newLLVM() (pkg.Artifact, string) {
|
||||
// symbols: clock_gettime, mallopt
|
||||
{"COMPILER_RT_INCLUDE_TESTS", "OFF"},
|
||||
|
||||
{"LLVM_LIT_ARGS", "'" + strings.Join([]string{
|
||||
"--verbose",
|
||||
"--filter-out='\\''" + strings.Join(skipChecks, "|") + "'\\''",
|
||||
}, " ") + "'"},
|
||||
{"LLVM_BUILD_TESTS", "ON"},
|
||||
{"LLVM_LIT_ARGS", litArgs(true, skipChecks...)},
|
||||
}...)
|
||||
}
|
||||
|
||||
return t.NewPackage("llvm", version, t.NewPatchedSource("llvm", version, newFromGitHub(
|
||||
"llvm/llvm-project",
|
||||
"llvmorg-"+version,
|
||||
checksum,
|
||||
), true, llvmPatches...), nil, &CMakeHelper{
|
||||
version := t.Version(llvmSource)
|
||||
return t.NewPackage("llvm", version, t.Load(llvmSource), &PackageAttr{
|
||||
Flag: TExclusive,
|
||||
}, &CMakeHelper{
|
||||
Append: []string{"llvm"},
|
||||
|
||||
Cache: cache,
|
||||
Script: `
|
||||
ln -s ld.lld /work/system/bin/ld
|
||||
|
||||
ln -s clang /work/system/bin/cc
|
||||
ln -s clang /work/system/bin/cpp
|
||||
ln -s clang++ /work/system/bin/c++
|
||||
`,
|
||||
|
||||
@@ -159,11 +314,31 @@ ninja ` + jobsFlagE + ` check-all
|
||||
|
||||
Zlib,
|
||||
Zstd,
|
||||
muslHeaders,
|
||||
early,
|
||||
KernelHeaders,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
const (
|
||||
version = "22.1.5"
|
||||
checksum = "32gOaLPHcUlo3hkdk5RbFumTE01XKeCAYZcpvn8IDHF95egXVfDFSl6eZL3ChMen"
|
||||
)
|
||||
|
||||
artifactsM[llvmSource] = Metadata{
|
||||
f: func(t Toolchain) (pkg.Artifact, string) {
|
||||
return t.NewPatchedSource("llvm", version, newFromGitHub(
|
||||
"llvm/llvm-project",
|
||||
"llvmorg-"+version,
|
||||
checksum,
|
||||
), true, llvmPatches...), version
|
||||
},
|
||||
|
||||
Name: "llvm-project",
|
||||
Description: "LLVM monorepo with Rosa OS patches",
|
||||
|
||||
ID: 1830,
|
||||
}
|
||||
|
||||
artifactsM[LLVM] = Metadata{
|
||||
f: Toolchain.newLLVM,
|
||||
|
||||
@@ -176,7 +351,5 @@ func init() {
|
||||
Zstd,
|
||||
Musl,
|
||||
},
|
||||
|
||||
ID: 1830,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,23 @@ index 8ac8d4eb9181..e46b04a898ca 100644
|
||||
addSystemInclude(DriverArgs, CC1Args, ResourceDirInclude);
|
||||
`},
|
||||
|
||||
{"path-system-libraries", `diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
index d525b417b4ea..fdc411f2239c 100644
|
||||
{"path-system-libraries", `diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
|
||||
index cb6a9b242421..b8d31690d1af 100644
|
||||
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
|
||||
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
|
||||
@@ -2314,6 +2314,10 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
|
||||
Prefixes.push_back("/opt/rh/devtoolset-2/root/usr");
|
||||
}
|
||||
|
||||
+ if (TargetTriple.getVendor() == llvm::Triple::Rosa) {
|
||||
+ Prefixes.push_back(concat(SysRoot, "/system"));
|
||||
+ }
|
||||
+
|
||||
// Fall back to /usr which is used by most non-Solaris systems.
|
||||
Prefixes.push_back(concat(SysRoot, "/usr"));
|
||||
}
|
||||
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
index d525b417b4ea..2b93f401733e 100644
|
||||
--- a/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
|
||||
@@ -302,6 +302,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
@@ -102,18 +117,7 @@ index d525b417b4ea..fdc411f2239c 100644
|
||||
|
||||
if (IsCSKY && !SelectedMultilibs.empty())
|
||||
SysRoot = SysRoot + SelectedMultilibs.back().osSuffix();
|
||||
@@ -337,12 +338,23 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
const std::string OSLibDir = std::string(getOSLibDir(Triple, Args));
|
||||
const std::string MultiarchTriple = getMultiarchTriple(D, Triple, SysRoot);
|
||||
|
||||
+ if (IsRosa) {
|
||||
+ ExtraOpts.push_back("-rpath");
|
||||
+ ExtraOpts.push_back("/system/lib");
|
||||
+ ExtraOpts.push_back("-rpath");
|
||||
+ ExtraOpts.push_back(concat("/system/lib", Triple.str()));
|
||||
+ }
|
||||
+
|
||||
// mips32: Debian multilib, we use /libo32, while in other case, /lib is
|
||||
@@ -341,8 +342,12 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
// used. We need add both libo32 and /lib.
|
||||
if (Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel) {
|
||||
Generic_GCC::AddMultilibPaths(D, SysRoot, "libo32", MultiarchTriple, Paths);
|
||||
@@ -128,7 +132,7 @@ index d525b417b4ea..fdc411f2239c 100644
|
||||
}
|
||||
Generic_GCC::AddMultilibPaths(D, SysRoot, OSLibDir, MultiarchTriple, Paths);
|
||||
|
||||
@@ -360,18 +372,30 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
@@ -360,18 +365,30 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args)
|
||||
Paths);
|
||||
}
|
||||
|
||||
@@ -164,7 +168,7 @@ index d525b417b4ea..fdc411f2239c 100644
|
||||
}
|
||||
|
||||
ToolChain::RuntimeLibType Linux::GetDefaultRuntimeLibType() const {
|
||||
@@ -572,6 +596,9 @@ std::string Linux::getDynamicLinker(const ArgList &Args) const {
|
||||
@@ -572,6 +589,9 @@ std::string Linux::getDynamicLinker(const ArgList &Args) const {
|
||||
return Triple.isArch64Bit() ? "/system/bin/linker64" : "/system/bin/linker";
|
||||
}
|
||||
if (Triple.isMusl()) {
|
||||
@@ -174,18 +178,5 @@ index d525b417b4ea..fdc411f2239c 100644
|
||||
std::string ArchName;
|
||||
bool IsArm = false;
|
||||
|
||||
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
|
||||
index f484d6f33ad8..dca55e72d67e 100644
|
||||
--- a/clang/tools/clang-installapi/Options.cpp
|
||||
+++ b/clang/tools/clang-installapi/Options.cpp
|
||||
@@ -514,7 +514,7 @@ bool Options::processFrontendOptions(InputArgList &Args) {
|
||||
FEOpts.FwkPaths = std::move(FrameworkPaths);
|
||||
|
||||
// Add default framework/library paths.
|
||||
- PathSeq DefaultLibraryPaths = {"/usr/lib", "/usr/local/lib"};
|
||||
+ PathSeq DefaultLibraryPaths = {"/usr/lib", "/system/lib", "/usr/local/lib"};
|
||||
PathSeq DefaultFrameworkPaths = {"/Library/Frameworks",
|
||||
"/System/Library/Frameworks"};
|
||||
|
||||
`},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newLMSensors() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "3-6-2"
|
||||
checksum = "7JYNutrihe-FP6r3ftf96uFZJJWPfxnBHL0ALSMA-vovaXVRr-sAjlLitw7WWpCI"
|
||||
)
|
||||
return t.NewPackage("lm_sensors", version, newFromGitHub(
|
||||
"lm-sensors/lm-sensors",
|
||||
"V"+version,
|
||||
checksum,
|
||||
), &PackageAttr{
|
||||
Writable: true,
|
||||
Chmod: true,
|
||||
EnterSource: true,
|
||||
|
||||
ScriptEarly: `
|
||||
ln -s \
|
||||
../../system/bin/perl \
|
||||
/usr/bin/
|
||||
`,
|
||||
}, &MakeHelper{
|
||||
InPlace: true,
|
||||
SkipConfigure: true,
|
||||
|
||||
Make: []string{
|
||||
"CC=cc",
|
||||
"ETCDIR=/system/etc",
|
||||
"PREFIX=/system",
|
||||
},
|
||||
|
||||
Check: []string{
|
||||
"CC=cc",
|
||||
"check",
|
||||
},
|
||||
|
||||
Install: "make DESTDIR=/work PREFIX=/system install",
|
||||
},
|
||||
Perl,
|
||||
PerlTestCmd,
|
||||
|
||||
M4,
|
||||
Bison,
|
||||
Flex,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[LMSensors] = Metadata{
|
||||
f: Toolchain.newLMSensors,
|
||||
|
||||
Name: "lm_sensors",
|
||||
Description: "user-space support for hardware monitoring drivers",
|
||||
Website: "https://hwmon.wiki.kernel.org/lm_sensors",
|
||||
|
||||
ID: 1831,
|
||||
}
|
||||
}
|
||||
+201
-2
@@ -1,6 +1,10 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
|
||||
func (t Toolchain) newLibglvnd() (pkg.Artifact, string) {
|
||||
const (
|
||||
@@ -12,8 +16,22 @@ func (t Toolchain) newLibglvnd() (pkg.Artifact, string) {
|
||||
"glvnd/libglvnd",
|
||||
"v"+version,
|
||||
checksum,
|
||||
), nil, (*MesonHelper)(nil),
|
||||
), nil, &MesonHelper{
|
||||
Setup: []KV{
|
||||
{"Dx11", "enabled"},
|
||||
{"Dglx", "enabled"},
|
||||
},
|
||||
ScriptCompiled: `
|
||||
export DISPLAY=':0'
|
||||
Xvfb &
|
||||
XVFB_PID="$!"
|
||||
trap 'kill $XVFB_PID && wait $XVFB_PID' EXIT
|
||||
`,
|
||||
},
|
||||
Binutils, // symbols check fail with llvm nm
|
||||
Xserver, // test suite wants X server
|
||||
|
||||
LibXext,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
@@ -24,6 +42,10 @@ func init() {
|
||||
Description: "The GL Vendor-Neutral Dispatch library",
|
||||
Website: "https://gitlab.freedesktop.org/glvnd/libglvnd",
|
||||
|
||||
Dependencies: P{
|
||||
LibXext,
|
||||
},
|
||||
|
||||
ID: 12098,
|
||||
}
|
||||
}
|
||||
@@ -64,3 +86,180 @@ func init() {
|
||||
ID: 1596,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newLibva() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "2.23.0"
|
||||
checksum = "UmF5tPyWIG_w5kiR3KFpoYbF7UUcaak5tyc-RhOheNTwQlLkPlifreFYCM9FQxbq"
|
||||
)
|
||||
return t.NewPackage("libva", version, newFromGitHub(
|
||||
"intel/libva",
|
||||
version,
|
||||
checksum,
|
||||
), nil, &MesonHelper{
|
||||
Setup: []KV{
|
||||
{"Dwith_x11", "yes"},
|
||||
{"Dwith_glx", "yes"},
|
||||
{"Dwith_wayland", "yes"},
|
||||
},
|
||||
},
|
||||
Libdrm,
|
||||
LibXfixes,
|
||||
Libglvnd,
|
||||
Wayland,
|
||||
KernelHeaders,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Libva] = Metadata{
|
||||
f: Toolchain.newLibva,
|
||||
|
||||
Name: "libva",
|
||||
Description: "an implementation for VA-API (Video Acceleration API)",
|
||||
Website: "https://01.org/vaapi",
|
||||
|
||||
Dependencies: P{
|
||||
Libdrm,
|
||||
LibXfixes,
|
||||
Libglvnd,
|
||||
Wayland,
|
||||
},
|
||||
|
||||
ID: 1752,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newMesa() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "26.1.0"
|
||||
checksum = "zU0fjqevySBaoi_5SLW3e2UffmGeBdxOuHmAHTH68n2hV-sjYoqg30koLqFXuk5y"
|
||||
)
|
||||
return t.NewPackage("mesa", version, newFromGitLab(
|
||||
"gitlab.freedesktop.org",
|
||||
"mesa/mesa",
|
||||
"mesa-"+version,
|
||||
checksum,
|
||||
), nil, &MesonHelper{
|
||||
Setup: []KV{
|
||||
{"Dplatforms", "x11,wayland"},
|
||||
{"Dvideo-codecs", "all"},
|
||||
|
||||
{"Dglvnd", "enabled"},
|
||||
{"Dgbm", "enabled"},
|
||||
|
||||
{"Dgallium-drivers", strings.Join([]string{
|
||||
"asahi", // Apple AGX
|
||||
"crocus", // Intel legacy
|
||||
"etnaviv", // Vivante GPU designs (mostly NXP/Marvell SoCs)
|
||||
"freedreno", // Qualcomm Adreno (all Qualcomm SoCs)
|
||||
"i915", // Intel extra legacy
|
||||
"iris", // new Intel (Broadwell+)
|
||||
"lima", // ARM Mali 4xx
|
||||
"llvmpipe", // software renderer
|
||||
"nouveau", // Nvidia
|
||||
"panfrost", // ARM Mali Midgard and up (T/G series)
|
||||
"r300", // very old AMD
|
||||
"r600", // less old AMD
|
||||
"radeonsi", // new AMD (GCN+)
|
||||
"softpipe", // older software renderer
|
||||
"svga", // VMWare virtualized GPU
|
||||
"tegra", // Nvidia Tegra SoCs
|
||||
"v3d", // Broadcom VC5 (Raspberry Pi 4)
|
||||
"vc4", // Broadcom VC4 (Raspberry Pi 0-3)
|
||||
"virgl", // QEMU virtualized GPU (aka VirGL)
|
||||
"zink", // generic OpenGL over Vulkan, experimental
|
||||
|
||||
// d3d12: WSL emulated GPU (aka Dozen)
|
||||
// ethosu: accelerator
|
||||
// rocket: accelerator
|
||||
}, ",")},
|
||||
|
||||
{"Dvulkan-drivers", strings.Join([]string{
|
||||
"amd", // AMD (aka RADV)
|
||||
"broadcom", // Broadcom VC5 (Raspberry Pi 4, aka V3D)
|
||||
"freedreno", // Qualcomm Adreno (all Qualcomm SoCs)
|
||||
"intel", // new Intel (aka ANV)
|
||||
"intel_hasvk", // Intel Haswell/Broadwell, "legacy" Vulkan driver (https://www.phoronix.com/news/Intel-HasVK-Drop-Dead-Code)
|
||||
"panfrost", // ARM Mali Midgard and up (T/G series)
|
||||
"swrast", // software renderer (aka Lavapipe)
|
||||
"virtio", // QEMU virtualized GPU (aka VirGL)
|
||||
"imagination", // PowerVR Rogue
|
||||
"asahi", // Apple AGX
|
||||
"gfxstream", // Android virtualized GPU
|
||||
|
||||
// nouveau: Nouveau (aka NVK), requires rust
|
||||
// microsoft-experimental: WSL virtualized GPU (aka DZN/Dozen)
|
||||
// kosmickrisp: macOS-specific
|
||||
}, ",")},
|
||||
{"Dvulkan-layers", strings.Join([]string{
|
||||
"device-select",
|
||||
"intel-nullhw",
|
||||
"overlay",
|
||||
"screenshot",
|
||||
"anti-lag",
|
||||
"vram-report-limit",
|
||||
}, ",")},
|
||||
|
||||
{"Dfreedreno-kmds", "msm,virtio"},
|
||||
{"Damdgpu-virtio", "true"},
|
||||
},
|
||||
},
|
||||
M4,
|
||||
PythonPackaging,
|
||||
PythonMako,
|
||||
PythonPyYAML,
|
||||
PythonPycparser,
|
||||
Glslang,
|
||||
SPIRVLLVMTranslator,
|
||||
|
||||
Zlib,
|
||||
Zstd,
|
||||
Gzip,
|
||||
Ncurses,
|
||||
Libglvnd,
|
||||
Libexpat,
|
||||
Libva,
|
||||
Libdrm,
|
||||
Elfutils,
|
||||
Bison,
|
||||
Flex,
|
||||
LMSensors,
|
||||
Libconfig,
|
||||
LibdisplayInfo,
|
||||
Wayland,
|
||||
WaylandProtocols,
|
||||
Libxshmfence,
|
||||
LibXxf86vm,
|
||||
LibXrandr,
|
||||
LibxcbUtilKeysyms,
|
||||
Libpng,
|
||||
Libarchive,
|
||||
KernelHeaders,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Mesa] = Metadata{
|
||||
f: Toolchain.newMesa,
|
||||
|
||||
Name: "mesa",
|
||||
Description: "open source implementations of OpenGL, OpenGL ES, Vulkan, OpenCL, and more",
|
||||
Website: "https://mesa3d.org",
|
||||
|
||||
Dependencies: P{
|
||||
Libdrm,
|
||||
Elfutils,
|
||||
LMSensors,
|
||||
LibdisplayInfo,
|
||||
Wayland,
|
||||
Libxshmfence,
|
||||
LibXxf86vm,
|
||||
LibXrandr,
|
||||
LibxcbUtilKeysyms,
|
||||
Libpng,
|
||||
},
|
||||
|
||||
ID: 1970,
|
||||
|
||||
latest: (*Versions).getStable,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ cd "$(mktemp -d)"
|
||||
meson setup \
|
||||
` + strings.Join(slices.Collect(func(yield func(string) bool) {
|
||||
for _, v := range append([]KV{
|
||||
{"wrap-mode", "nodownload"},
|
||||
{"wrap-mode", "nofallback"},
|
||||
{"prefix", "/system"},
|
||||
{"buildtype", "release"},
|
||||
}, attr.Setup...) {
|
||||
|
||||
@@ -7,6 +7,10 @@ func (t Toolchain) newMksh() (pkg.Artifact, string) {
|
||||
version = "59c"
|
||||
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,
|
||||
Perl,
|
||||
Coreutils,
|
||||
@@ -18,8 +22,7 @@ cd "$(mktemp -d)"
|
||||
sh /usr/src/mksh/Build.sh -r
|
||||
CPPFLAGS="${CPPFLAGS} -DMKSH_BINSHPOSIX -DMKSH_BINSHREDUCED" \
|
||||
sh /usr/src/mksh/Build.sh -r -L
|
||||
./test.sh -C regress:no-ctty
|
||||
|
||||
`+scriptTest+`
|
||||
mkdir -p /work/system/bin/
|
||||
cp -v mksh /work/system/bin/
|
||||
cp -v lksh /work/system/bin/sh
|
||||
|
||||
@@ -53,6 +53,23 @@ rmdir -v /work/lib
|
||||
Chmod: true,
|
||||
|
||||
Env: env,
|
||||
|
||||
Patches: []KV{
|
||||
{"ldso-rosa", `diff --git a/ldso/dynlink.c b/ldso/dynlink.c
|
||||
index 715948f4..c2fece68 100644
|
||||
--- a/ldso/dynlink.c
|
||||
+++ b/ldso/dynlink.c
|
||||
@@ -1157,7 +1157,7 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
|
||||
sys_path = "";
|
||||
}
|
||||
}
|
||||
- if (!sys_path) sys_path = "/lib:/usr/local/lib:/usr/lib";
|
||||
+ if (!sys_path) sys_path = "/system/lib:/system/lib/" LDSO_ARCH "-rosa-linux-musl:/lib:/usr/local/lib:/usr/lib";
|
||||
fd = path_open(name, sys_path, buf, sizeof buf);
|
||||
}
|
||||
pathname = buf;
|
||||
`},
|
||||
},
|
||||
}, &helper,
|
||||
Coreutils,
|
||||
), version
|
||||
|
||||
@@ -382,3 +382,27 @@ func init() {
|
||||
ID: 3549,
|
||||
}
|
||||
}
|
||||
|
||||
func (t Toolchain) newPerlTestCmd() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.09"
|
||||
checksum = "gpGUwyC9IozDiYSgW_kXARNfXsTPFa6cTowJmmCBbPqcs2-pONZca_SB06FGy-7H"
|
||||
)
|
||||
return t.newViaPerlMakeMaker("Test::Cmd", version, newFromCPAN(
|
||||
"NEILB",
|
||||
"Test-Cmd",
|
||||
version,
|
||||
checksum,
|
||||
), nil), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[PerlTestCmd] = Metadata{
|
||||
f: Toolchain.newPerlTestCmd,
|
||||
|
||||
Name: "perl-Test::Cmd",
|
||||
Description: "portable testing of commands and scripts",
|
||||
Website: "https://metacpan.org/release/Test-Cmd",
|
||||
|
||||
ID: 6014,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newPixman() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "0.46.4"
|
||||
checksum = "iECDxLG9SxUrvGHqeDoaBa-b3uqdT5DC4zudjtrwb8Wodq82pyacmFNEAo4SDsiE"
|
||||
)
|
||||
return t.NewPackage("pixman", version, newFromGitLab(
|
||||
"gitlab.freedesktop.org",
|
||||
"pixman/pixman",
|
||||
"pixman-"+version,
|
||||
checksum,
|
||||
), nil, &MesonHelper{
|
||||
Setup: []KV{
|
||||
{"Dtests", "enabled"},
|
||||
},
|
||||
}), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[Pixman] = Metadata{
|
||||
f: Toolchain.newPixman,
|
||||
|
||||
Name: "pixman",
|
||||
Description: "a low-level software library for pixel manipulation",
|
||||
Website: "https://pixman.org/",
|
||||
|
||||
ID: 3648,
|
||||
}
|
||||
}
|
||||
+48
-4
@@ -9,8 +9,8 @@ import (
|
||||
|
||||
func (t Toolchain) newPython() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "3.14.4"
|
||||
checksum = "X0VRAAGOlCVldh4J9tRAE-YrJtDvqfQTJaqxKPXNX6YTPlwpR9GwA5WRIZDO-63s"
|
||||
version = "3.14.5"
|
||||
checksum = "zYIpDlk2ftZ-UVGCQS1rthle2OHoyXV653ztWiopKV1NhmIJf1K2hHbkwM4DozQ9"
|
||||
)
|
||||
return t.NewPackage("python", version, newTar(
|
||||
"https://www.python.org/ftp/python/"+version+
|
||||
@@ -351,6 +351,29 @@ func init() {
|
||||
)
|
||||
}
|
||||
|
||||
func init() {
|
||||
artifactsM[LIT] = Metadata{
|
||||
f: func(t Toolchain) (pkg.Artifact, string) {
|
||||
version := t.Version(LLVM)
|
||||
return t.NewPackage("lit", version, t.Load(llvmSource), nil, &PipHelper{
|
||||
Append: []string{"llvm", "utils", "lit"},
|
||||
// already checked during llvm
|
||||
SkipCheck: true,
|
||||
},
|
||||
PythonSetuptools,
|
||||
), version
|
||||
},
|
||||
|
||||
Name: "lit",
|
||||
Description: "a portable tool for executing LLVM and Clang style test suites",
|
||||
Website: "https://llvm.org/docs/CommandGuide/lit.html",
|
||||
|
||||
Dependencies: P{
|
||||
Python,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
const (
|
||||
version = "1.1.1"
|
||||
@@ -372,8 +395,8 @@ func init() {
|
||||
|
||||
func init() {
|
||||
const (
|
||||
version = "2026.4.28.13"
|
||||
checksum = "Z3MbmMXtmWHCM3-EvJehb9MzDqX7Ce_Xg86D5g5nxFRWMKqwHwnQ8R-AlKf-32HU"
|
||||
version = "2026.5.7.17"
|
||||
checksum = "1Fcps0gK9P4ofwGL8MISN9k1Q40-quxX7NDpIna50TmziBNrZy-0Vz0I9yIeHCoP"
|
||||
)
|
||||
artifactsM[PythonTroveClassifiers] = newPythonPackage(
|
||||
"trove-classifiers", 88298,
|
||||
@@ -561,3 +584,24 @@ func init() {
|
||||
}, P{PythonSetuptools},
|
||||
)
|
||||
}
|
||||
|
||||
func init() {
|
||||
const (
|
||||
version = "3.00"
|
||||
checksum = "4qfCMFKp0fLsRsloOAF780tXX_Ce_68RwinCmjNGObAX32WpF_iBafIKW1S1bYlA"
|
||||
)
|
||||
artifactsM[PythonPycparser] = newPythonPackage(
|
||||
"pycparser", 8175,
|
||||
"complete C99 parser in pure Python",
|
||||
"https://github.com/eliben/pycparser",
|
||||
version, newFromGitHub(
|
||||
"eliben/pycparser",
|
||||
"release_v"+version, checksum,
|
||||
), &PackageAttr{
|
||||
// test case hard codes gcc
|
||||
ScriptEarly: `
|
||||
ln -s clang /system/bin/gcc
|
||||
`,
|
||||
}, nil, P{PythonSetuptools},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ EOF
|
||||
{"disable-download"},
|
||||
{"disable-docs"},
|
||||
|
||||
{"static"},
|
||||
{"target-list-exclude", "" +
|
||||
// fails to load firmware
|
||||
"ppc-linux-user," +
|
||||
|
||||
+16
-11
@@ -4,7 +4,6 @@ package rosa
|
||||
import (
|
||||
"errors"
|
||||
"path"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -54,10 +53,9 @@ var (
|
||||
AbsSystem = fhs.AbsRoot.Append("system")
|
||||
)
|
||||
|
||||
// linuxArch returns the architecture name used by linux corresponding to
|
||||
// [runtime.GOARCH].
|
||||
// linuxArch returns the architecture name used by linux corresponding to arch.
|
||||
func linuxArch() string {
|
||||
switch runtime.GOARCH {
|
||||
switch arch {
|
||||
case "amd64":
|
||||
return "x86_64"
|
||||
case "arm64":
|
||||
@@ -66,11 +64,11 @@ func linuxArch() string {
|
||||
return "riscv64"
|
||||
|
||||
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 {
|
||||
return linuxArch() + "-rosa-linux-musl"
|
||||
}
|
||||
@@ -80,9 +78,9 @@ type perArch[T any] map[string]T
|
||||
|
||||
// unwrap returns the value for the current architecture.
|
||||
func (p perArch[T]) unwrap() T {
|
||||
v, ok := p[runtime.GOARCH]
|
||||
v, ok := p[arch]
|
||||
if !ok {
|
||||
panic("unsupported target " + runtime.GOARCH)
|
||||
panic("unsupported target " + arch)
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -222,6 +220,8 @@ const (
|
||||
TEarly
|
||||
// TNoToolchain excludes the LLVM toolchain.
|
||||
TNoToolchain
|
||||
// THostNet arranges for a [pkg.KindExecNet] to be created.
|
||||
THostNet
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -268,7 +268,7 @@ func (t Toolchain) New(
|
||||
support = append(support, extra...)
|
||||
support = append(support, cureEtc{})
|
||||
if t == toolchainStage0 {
|
||||
support = append(support, NewStage0())
|
||||
support = append(support, t.Load(stage0Dist))
|
||||
} else {
|
||||
support = append(support, _toolchainBusybox.New("gentoo", 0, nil, nil, nil, `
|
||||
tar -C /work -xf /usr/src/stage3.tar.xz
|
||||
@@ -327,7 +327,9 @@ mkdir -vp /work/system/bin
|
||||
}
|
||||
|
||||
return pkg.NewExec(
|
||||
name, knownChecksum, pkg.ExecTimeoutMax, flag&TExclusive != 0,
|
||||
name, arch, knownChecksum, pkg.ExecTimeoutMax,
|
||||
flag&THostNet != 0,
|
||||
flag&TExclusive != 0,
|
||||
fhs.AbsRoot, env,
|
||||
AbsSystem.Append("bin", "sh"),
|
||||
[]string{"sh", absCureScript.String()},
|
||||
@@ -410,6 +412,9 @@ type Helper interface {
|
||||
|
||||
// PackageAttr holds build-system-agnostic attributes.
|
||||
type PackageAttr struct {
|
||||
// Measure output if populated. Required by [THostNet].
|
||||
KnownChecksum *pkg.Checksum
|
||||
|
||||
// Mount the source tree writable.
|
||||
Writable bool
|
||||
// Do not pass through [Toolchain.NewPatchedSource].
|
||||
@@ -547,7 +552,7 @@ cd '/usr/src/` + name + `/'
|
||||
name+"-"+version,
|
||||
attr.Flag,
|
||||
extraRes,
|
||||
nil,
|
||||
attr.KnownChecksum,
|
||||
attr.Env,
|
||||
scriptEarly+helper.script(name),
|
||||
slices.Concat(attr.Paths, []pkg.ExecPath{
|
||||
|
||||
@@ -28,7 +28,7 @@ var (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
rosa.DropCaches(rosa.OptLLVMNoLTO)
|
||||
rosa.DropCaches("", rosa.OptLLVMNoLTO)
|
||||
container.TryArgv0(nil)
|
||||
|
||||
code := m.Run()
|
||||
@@ -61,7 +61,7 @@ func getCache(t *testing.T) *pkg.Cache {
|
||||
msg := message.New(log.New(os.Stderr, "rosa: ", 0))
|
||||
msg.SwapVerbose(true)
|
||||
|
||||
if buildTestCache, err = pkg.Open(ctx, msg, 0, 0, 0, a); err != nil {
|
||||
if buildTestCache, err = pkg.Open(ctx, msg, pkg.CSuppressInit, 0, 0, a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
@@ -94,14 +94,14 @@ func TestCureAll(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkStage3(b *testing.B) {
|
||||
flags := rosa.Flags()
|
||||
b.Cleanup(func() { rosa.DropCaches(flags) })
|
||||
arch, flags := rosa.Arch(), rosa.Flags()
|
||||
b.Cleanup(func() { rosa.DropCaches(arch, flags) })
|
||||
|
||||
for b.Loop() {
|
||||
rosa.Std.Load(rosa.LLVM)
|
||||
|
||||
b.StopTimer()
|
||||
rosa.DropCaches(0)
|
||||
rosa.DropCaches("", 0)
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
+27
-21
@@ -1,8 +1,6 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"hakurei.app/fhs"
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
@@ -32,24 +30,32 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// stage0 stores the tarball unpack artifact.
|
||||
stage0 pkg.Artifact
|
||||
// stage0Once is for lazy initialisation of stage0.
|
||||
stage0Once sync.Once
|
||||
)
|
||||
func init() {
|
||||
const version = "20260504"
|
||||
artifactsM[stage0Dist] = Metadata{
|
||||
f: func(Toolchain) (pkg.Artifact, string) {
|
||||
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].
|
||||
func NewStage0() pkg.Artifact {
|
||||
stage0Once.Do(func() {
|
||||
stage0 = newTar(
|
||||
"https://hakurei.app/seed/20260429/"+
|
||||
"stage0-"+triplet()+".tar.bz2",
|
||||
perArch[string]{
|
||||
"amd64": "ldz-WkSx2wxUK4ndi-tlaaU8ykOowbpGRcBsciAcIDdnX6-QfzQg_se3lsZYuzuK",
|
||||
}.unwrap(),
|
||||
pkg.TarBzip2,
|
||||
)
|
||||
})
|
||||
return stage0
|
||||
Name: "stage0-dist",
|
||||
Description: "Rosa OS stage0 bootstrap seed",
|
||||
}
|
||||
}
|
||||
|
||||
// HasStage0 returns whether a stage0 distribution is available.
|
||||
func HasStage0() (ok bool) {
|
||||
func() {
|
||||
defer func() { ok = recover() == nil }()
|
||||
toolchainStage0.Load(stage0Dist)
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
package rosa
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"hakurei.app/internal/pkg"
|
||||
)
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newTamaGo() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "1.26.2"
|
||||
checksum = "5xlhWq2NGhYCjt0y73QkydJ386lxg6-HkiO84ne6ByQSJBDat7-HSVzNA6jy7Laz"
|
||||
version = "1.26.3"
|
||||
checksum = "-nH3MjAzDDLTeJ2hRKYJcJwo5-Ikci4zOHfB8j1vKn7zrF9TS6zYaoLi8qohGwAE"
|
||||
)
|
||||
return t.New("tamago-go"+version, 0, t.AppendPresets(nil,
|
||||
Bash,
|
||||
@@ -26,7 +22,7 @@ chmod -R +w ..
|
||||
|
||||
sed -i \
|
||||
'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 \
|
||||
's/cpu.X86.HasAVX512VBMI/& \&\& cpu.X86.HasPOPCNT/' \
|
||||
internal/runtime/gc/scan/scan_amd64.go
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package rosa
|
||||
|
||||
import "hakurei.app/internal/pkg"
|
||||
|
||||
func (t Toolchain) newVIM() (pkg.Artifact, string) {
|
||||
const (
|
||||
version = "9.2.0461"
|
||||
checksum = "18Rr_5oIf_PkKuqVkN4CMZIGkZEgpN1vamlrsvPLBjn4mN98CRuoJmhzRZ7MoVYM"
|
||||
)
|
||||
return t.NewPackage("vim", version, newFromGitHub(
|
||||
"vim/vim",
|
||||
"v"+version,
|
||||
checksum,
|
||||
), &PackageAttr{
|
||||
Chmod: true,
|
||||
Writable: true,
|
||||
EnterSource: true,
|
||||
}, &MakeHelper{
|
||||
InPlace: true,
|
||||
Configure: []KV{
|
||||
{"with-tlib", "ncursesw"},
|
||||
},
|
||||
Check: []string{"test"},
|
||||
|
||||
// very expensive
|
||||
SkipCheck: true,
|
||||
},
|
||||
Ncurses,
|
||||
), version
|
||||
}
|
||||
func init() {
|
||||
artifactsM[VIM] = Metadata{
|
||||
f: Toolchain.newVIM,
|
||||
|
||||
Name: "vim",
|
||||
Description: "a greatly improved version of the good old UNIX editor Vi",
|
||||
Website: "https://www.vim.org",
|
||||
|
||||
Dependencies: P{
|
||||
Ncurses,
|
||||
},
|
||||
|
||||
ID: 5092,
|
||||
}
|
||||
}
|
||||
+989
-15
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user