diff options
Diffstat (limited to 'internal')
29 files changed, 278 insertions, 112 deletions
diff --git a/internal/outcome/process.go b/internal/outcome/process.go index 614b22fc..599af8f2 100644 --- a/internal/outcome/process.go +++ b/internal/outcome/process.go @@ -303,7 +303,7 @@ func (k *outcome) main(msg message.Msg, identifierFd int) { ) for eh := range entries { var et hst.Enablements - if et, err = eh.Load(nil); err != nil { + if et, err = eh.Load(nil, syscall.Kill); err != nil { perror(err, "read state header of instance "+eh.ID.String()) } else { rt |= et diff --git a/internal/rosa/azalea/evaluate.go b/internal/rosa/azalea/evaluate.go index b35a056a..26a02c02 100644 --- a/internal/rosa/azalea/evaluate.go +++ b/internal/rosa/azalea/evaluate.go @@ -365,7 +365,13 @@ func evaluateAny(d PF, s []Frame, expr, rp any) bool { panic(ErrInvalidSpecial) } farg.K = names[0] - if err := storeE(&farg.V, arg.V[0]); err != nil { + + v := arg.V[0] + if p, _ok := v.(Ident); _ok && p == "nil" { + v = Nil{} + } + + if err := storeE(&farg.V, v); err != nil { panic(err) } fargs = append(fargs, farg) diff --git a/internal/rosa/package/curl.az b/internal/rosa/package/curl.az index eee21bdd..58a0ad72 100644 --- a/internal/rosa/package/curl.az +++ b/internal/rosa/package/curl.az @@ -3,10 +3,10 @@ package curl { website = "https://curl.se"; anitya = 381; - version# = "8.21.0"; + version# = "8.22.0"; source = remoteTar { url = "https://curl.se/download/curl-"+version+".tar.bz2"; - checksum = "lJSm8bVjS0OmsarEdbvejdQdvXsb7yGarlr6oMtA9FW1EXOga8zZxa1LPtfaq_qX"; + checksum = "zeIqlmHElDa3IPLZq86ICRxn1YoiXlNCdx30BuahcUToYqjSuyvu8AtRqbzKH_06"; compress = bzip2; }; diff --git a/internal/rosa/package/foot.az b/internal/rosa/package/foot.az index e3166db7..78ec76bb 100644 --- a/internal/rosa/package/foot.az +++ b/internal/rosa/package/foot.az @@ -3,11 +3,11 @@ package foot { website = "https://codeberg.org/dnkl/foot"; anitya = 141611; - version# = "1.27.0"; + version# = "1.28.0"; source = remoteTar { url = "https://codeberg.org/dnkl/foot/releases/download/"+version+ "/foot-"+version+".tar.gz"; - checksum = "22rG3rMuQI8Ae2XTcZgOJ-6AbF7Taex8XpgvD6Fyg1Mn5Va67FfUcK3tRaq61TFg"; + checksum = "w16syUlOAYlsJZ-a4QY9dfu4ph3yFypw05RXocNmTrefghzmT-HhV1O89QPAj5mV"; compress = gzip; }; diff --git a/internal/rosa/package/gnu.az b/internal/rosa/package/gnu.az index b3ed798c..b2a0fd8f 100644 --- a/internal/rosa/package/gnu.az +++ b/internal/rosa/package/gnu.az @@ -507,10 +507,10 @@ package parallel { website = "https://www.gnu.org/software/parallel"; anitya = 5448; - version# = "20260722"; + version# = "20260822"; source = remoteTar { url = "https://mirrors.kernel.org/gnu/parallel/parallel-"+version+".tar.bz2"; - checksum = "liG9pBSRqUPqDuRXmnJPoVh-G53lDKkkO8GGabSxQnSho4b9BD0EaJDdScgVhnwj"; + checksum = "zf-5ZEpAZ073coMSyOY7NRmXwCRPWvpjbjOMOvirU9fHlU0WWovwss-bAMrh0q_G"; compress = bzip2; }; diff --git a/internal/rosa/package/go/package.az b/internal/rosa/package/go.az index d06720b5..6efca245 100644 --- a/internal/rosa/package/go/package.az +++ b/internal/rosa/package/go.az @@ -3,13 +3,12 @@ package go { website = "https://go.dev"; anitya = 1227; - version# = "1.27.0"; + version# = "1.27.1"; source = remoteTar { url = "https://go.dev/dl/go"+version+".src.tar.gz"; - checksum = "Ookl2vP6KMfJuxaxX6nXZ1gnL43wZPl5GjpVsrI7IJiB4b3xr1PbnbYIOJ8v5jzG"; + checksum = "Jw7OCTZbixLPF0E2WafFBh6DnxbsElvEg_RsMGkrzAmi0pyJTI3eQqLJsvXlVtbK"; compress = gzip; }; - patches = [ "820480.patch" ]; env = [ "CC=cc", diff --git a/internal/rosa/package/go/820480.patch b/internal/rosa/package/go/820480.patch deleted file mode 100644 index 05459a80..00000000 --- a/internal/rosa/package/go/820480.patch +++ /dev/null @@ -1,24 +0,0 @@ -From fa10bbcce5635dd533ba933252e9dabd8a7f5e2a Mon Sep 17 00:00:00 2001 -From: David Chase <drchase@google.com> -Date: Mon, 24 Aug 2026 15:20:40 -0400 -Subject: [PATCH] simd/archsimd: added PMULL gate on carryless multiply test - -Fixes #80991. - -Change-Id: I637de72acbb338c4c3b0a47f4a5520c33fe81fd2 ---- - -diff --git a/src/simd/archsimd/internal/simd_test/simd_arm64_test.go b/src/simd/archsimd/internal/simd_test/simd_arm64_test.go -index f009488..7836fc8 100644 ---- a/src/simd/archsimd/internal/simd_test/simd_arm64_test.go -+++ b/src/simd/archsimd/internal/simd_test/simd_arm64_test.go -@@ -22,6 +22,9 @@ - } - - func TestClMul(t *testing.T) { -+ if !archsimd.ARM64.PMULL() { -+ t.Skip("no carryless multiply") -+ } - var x = archsimd.LoadUint64x2([]uint64{1, 5}) - var y = archsimd.LoadUint64x2([]uint64{3, 9}) - diff --git a/internal/rosa/package/hakurei.az b/internal/rosa/package/hakurei.az index 24a2a7c9..0448c0f3 100644 --- a/internal/rosa/package/hakurei.az +++ b/internal/rosa/package/hakurei.az @@ -2,11 +2,11 @@ package hakurei-source { description = "hakurei source tree"; exclude = true; - version# = "0.4.7"; + version# = "0.4.8"; output = remoteGit { url = "https://git.gensokyo.uk/rosa/hakurei.git"; tag = "v"+version; - checksum = "gQs-fBuPsYjGEBgxe3pSiqa7aMrqUkimi6iyjrhR6YKrOiZLfd4bFwiB9V8k3cKO"; + checksum = "B6aoojyXblZ7eYwdc3Ak3wDbBpu63j8S79Gpiul9Obb8diqzBIJ2qNCgWVH6qLsc"; }; } diff --git a/internal/rosa/package/hwdata.az b/internal/rosa/package/hwdata.az index b826953c..4d552d5d 100644 --- a/internal/rosa/package/hwdata.az +++ b/internal/rosa/package/hwdata.az @@ -3,11 +3,11 @@ package hwdata { website = "https://github.com/vcrhonek/hwdata"; anitya = 5387; - version# = "0.410"; + version# = "0.411"; source = remoteGitHub { suffix = "vcrhonek/hwdata"; tag = "v"+version; - checksum = "w1fbTz8NR7C6SYOupqTyNZGJHa_cDRAsGs4guZ3LddqlmawgEP_CWFw9-OYFKJ1q"; + checksum = "5YWsZC8NgZShWWHmC66xqEkTJLAUYJoDt8kmstJyLWfsNqAWAhJZGkVUfgqbTv-6"; }; writable = true; diff --git a/internal/rosa/package/iproute2.az b/internal/rosa/package/iproute2.az index 3a5829df..2a31fc4b 100644 --- a/internal/rosa/package/iproute2.az +++ b/internal/rosa/package/iproute2.az @@ -3,11 +3,11 @@ package iproute2 { website = "https://wiki.linuxfoundation.org/networking/iproute2"; anitya = 1392; - version# = "7.1.0"; + version# = "7.2.0"; source = remoteTar { url = "https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/"+ "snapshot/iproute2-"+version+".tar.gz"; - checksum = "yc6NdF6GtFUpKtKg6xwF_yxYGyj0eqFQW8RGsgBzKrOiVlE2bpVQlfeZf_ca5wJL"; + checksum = "VECMJeSm-lHWpyJqnI-1CeHFejAgi_mb8LJjMGIx5rqww6AlZii3g_tUGPLu4npo"; compress = gzip; }; diff --git a/internal/rosa/package/kernel/amd64.config b/internal/rosa/package/kernel/amd64.config index d560bdb8..b52bfbd4 100644 --- a/internal/rosa/package/kernel/amd64.config +++ b/internal/rosa/package/kernel/amd64.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86 6.18.47 Kernel Configuration +# Linux/x86 6.18.49 Kernel Configuration # CONFIG_CC_VERSION_TEXT="clang version 23.1.0" CONFIG_GCC_VERSION=0 diff --git a/internal/rosa/package/kernel/arm64.config b/internal/rosa/package/kernel/arm64.config index c8658775..9c6951b7 100644 --- a/internal/rosa/package/kernel/arm64.config +++ b/internal/rosa/package/kernel/arm64.config @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/arm64 6.18.47 Kernel Configuration +# Linux/arm64 6.18.49 Kernel Configuration # CONFIG_CC_VERSION_TEXT="clang version 23.1.0" CONFIG_GCC_VERSION=0 diff --git a/internal/rosa/package/kernel/package.az b/internal/rosa/package/kernel/package.az index e2f6339e..b3af55d3 100644 --- a/internal/rosa/package/kernel/package.az +++ b/internal/rosa/package/kernel/package.az @@ -3,11 +3,11 @@ package kernel-source { website = "https://kernel.org"; exclude = true; - version# = "6.18.48"; + version# = "6.18.49"; output = remoteTar { url = "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/"+ "snapshot/linux-"+version+".tar.gz"; - checksum = "XzcqKk0ODA4tTjl5tVQdckuLHVnITGalDH_EIYUC8WHimc7O6bbews-vFRHUK01m"; + checksum = "J0FT9987a7mlYBGcACXWeTsmfScbS8FM_JAG9LLl917UdlcFRP_UDj73yR82bvGR"; compress = gzip; }; } @@ -48,7 +48,7 @@ cat \ amd64 = "uwTZX3iLp6iPsR0-F0Jz2aUviLMMqv4xGQHoKeemo2HrNLD7fDlIDqqYT--i_5HJ"; arm64 = "A9EWzTGEChVLJVm5BP1GzpibdUddXUN2hIjAGkD-KQr5sc6RuEeIQle8VAiAYUqp"; - riscv64 = "IB1oG3tEAN15wjYl8S5ekYXVFYN7-rb3m2f1Kdi58dGqnrIBTG2r1qb1utb5D1lb"; + riscv64 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; }; inputs = [ rsync ]; } diff --git a/internal/rosa/package/libexpat.az b/internal/rosa/package/libexpat.az index 20e4f510..a79dcd30 100644 --- a/internal/rosa/package/libexpat.az +++ b/internal/rosa/package/libexpat.az @@ -3,7 +3,7 @@ package libexpat { website = "https://libexpat.github.io"; anitya = 770; - version# = "2.8.3"; + version# = "2.8.4"; source = remoteGitHubRelease { suffix = "libexpat/libexpat"; tag = "R_"+replace { @@ -12,7 +12,7 @@ package libexpat { new = "_"; }; name = "expat-"+version+".tar.bz2"; - checksum = "prHQc14YY3Gp6BfflneNcVyRXM-AxFCqiigxrGL9TT1sGhVODvI_9JNR2xnZlZSj"; + checksum = "Nbi2wl8U9KHf3LVLczfAb5Kqr7HFv_myReVjxKXUkfhToWfL-pP2o44fDpWHJ4ig"; compress = bzip2; }; diff --git a/internal/rosa/package/mesa/package.az b/internal/rosa/package/mesa/package.az index 7967e3d7..8db6462e 100644 --- a/internal/rosa/package/mesa/package.az +++ b/internal/rosa/package/mesa/package.az @@ -4,12 +4,12 @@ package mesa { anitya = 1970; latest = anityaFallback; - version# = "26.2.1"; + version# = "26.2.2"; source = remoteGitLab { domain = "gitlab.freedesktop.org"; suffix = "mesa/mesa"; ref = "mesa-"+version; - checksum = "WZYAysQFZt8KMFAxQheKFZHarR1isgOeSw4LyfqAcmaHwXMCLiVao6eGuEuhJTJv"; + checksum = "5mIdojhazcdAgbLW1v4d-7P5pIhu9wnGS3ek1QhJIPMJyFWhfKhiZCsd53WAHt1_"; }; patches = [ "include.patch" ]; diff --git a/internal/rosa/package/netfilter/package.az b/internal/rosa/package/netfilter/package.az index 3dea2b87..9b6fedc1 100644 --- a/internal/rosa/package/netfilter/package.az +++ b/internal/rosa/package/netfilter/package.az @@ -29,11 +29,11 @@ package libnftnl { website = "https://www.netfilter.org/projects/libnftnl"; anitya = 1681; - version# = "1.3.1"; + version# = "1.3.2"; source = remoteGit { url = "https://git.netfilter.org/libnftnl.git"; tag = "libnftnl-"+version; - checksum = "91ou66K-I17iX6DB6hiQkhhC_v4DFW5iDGzwjVRNbJNEmKqowLZBlh3FY-ZDO0r9"; + checksum = "bW_GokelPejp2SKOo_OgFFYjf-7rn3GL6x1Z4kZb9NkfZe20WrRLNjQWVxxS9oII"; }; env = [ diff --git a/internal/rosa/package/noto.az b/internal/rosa/package/noto.az index 64d27a58..e3bcc80b 100644 --- a/internal/rosa/package/noto.az +++ b/internal/rosa/package/noto.az @@ -3,11 +3,11 @@ package noto { website = "https://fonts.google.com/noto"; anitya = 10671; - version# = "2026.08.01"; + version# = "2026.09.01"; source = remoteGitHub { suffix = "notofonts/notofonts.github.io"; tag = "noto-monthly-release-"+version; - checksum = "xTuvGQBtw9FWrbMQ0OHC-EVpUWYwLxNHPtaind_t6nP37En0r8orIY9N0X_YrPH7"; + checksum = "ZITq5IyZNZovca3t7snPndDgjdzbCeBIoGYpY7bbnR-w9oAiz1vkN9zwRWz5JJw5"; }; enterSource = true; diff --git a/internal/rosa/package/pcre2.az b/internal/rosa/package/pcre2.az index 27f21bf7..0e7d1c68 100644 --- a/internal/rosa/package/pcre2.az +++ b/internal/rosa/package/pcre2.az @@ -3,12 +3,12 @@ package pcre2 { website = "https://pcre2project.github.io/pcre2"; anitya = 5832; - version# = "10.47"; + version# = "10.48"; source = remoteGitHubRelease { suffix = "PCRE2Project/pcre2"; tag = "pcre2-"+version; name = "pcre2-"+version+".tar.bz2"; - checksum = "IbC24vVayju6nB9EhrBPSDexk22wDecdpyrjgC3nCZXkwTnUjq4CD2q5sopqu6CW"; + checksum = "3HorftFDryXBh8JLSbEVWpU3OtSF_wUhtNZNCRvCDiGlBsMU150CxrDFKE8lVo6x"; compress = bzip2; }; diff --git a/internal/rosa/package/pipewire.az b/internal/rosa/package/pipewire.az index 1cbdeda9..2b971244 100644 --- a/internal/rosa/package/pipewire.az +++ b/internal/rosa/package/pipewire.az @@ -3,12 +3,12 @@ package wireplumber { website = "https://gitlab.freedesktop.org/pipewire/wireplumber"; anitya = 235056; - version# = "0.5.16"; + version# = "0.5.17"; source = remoteGitLab { domain = "gitlab.freedesktop.org"; suffix = "pipewire/wireplumber"; ref = version; - checksum = "Ftt6s3tt5eDlJPizOWsvjHRfOQezVHOp3sDATJr2_B6MORbFW9XgWSHUNdy-dIp0"; + checksum = "2vWxpOqnQmOlgMlNLYMu9ebPbF2y3W_eqDFM_bwAtCwnHhYE0S4kM8MkpFuXyVlu"; }; // OSError: [Errno 30] Read-only file system: '../../usr/src/wireplumber/po/conf.pot' diff --git a/internal/rosa/package/python/package.az b/internal/rosa/package/python/package.az index 525cb8c2..8182576d 100644 --- a/internal/rosa/package/python/package.az +++ b/internal/rosa/package/python/package.az @@ -197,11 +197,11 @@ package python-vcs-versioning { website = "https://setuptools-scm.readthedocs.io/en/latest"; anitya = 389421; - version# = "2.3.1"; + version# = "2.3.4"; source = remoteGitHub { suffix = "pypa/setuptools-scm"; tag = "vcs-versioning-v"+version; - checksum = "rcbu8LlWlY6jmhC95pfnEKgGQuRxHY8_Pvo2jjmlJWSlyHSs1tFIRQ_FoMfi8YXo"; + checksum = "TljjSQUxo07jMzfaVyieXD8dS_olgwEA2_RndGeIjlsdKWSa2oYZ5laMRjdgnXJS"; }; env = [ @@ -231,11 +231,11 @@ package python-setuptools-scm { website = "https://setuptools-scm.readthedocs.io/en/latest"; anitya = 7874; - version# = "10.2.1"; + version# = "10.2.3"; source = remoteGitHub { suffix = "pypa/setuptools-scm"; tag = "setuptools-scm-v"+version; - checksum = "XnfdKDHzgtY3imPPz1lA1H0QHnjXlN5ZI-er0I-kfUq4w_a6RzUq8FCOJNBOkpK1"; + checksum = "C76_jiIc_XanEZrpxLb47B3YArtmlt7HcpEOo0wZ5Z4mCFJb2HLIgOSOiRSfttpW"; }; env = [ diff --git a/internal/rosa/package/spirv.az b/internal/rosa/package/spirv.az index cefa4d0a..21c07689 100644 --- a/internal/rosa/package/spirv.az +++ b/internal/rosa/package/spirv.az @@ -50,11 +50,11 @@ package spirv-llvm-translator { website = "https://github.com/KhronosGroup/SPIRV-LLVM-Translator"; anitya = 227273; - version# = "23.1.0"; + version# = "23.1.1"; source = remoteGitHub { suffix = "KhronosGroup/SPIRV-LLVM-Translator"; tag = "v"+version; - checksum = "bKflf64sXw0NDhtqE_gT5cHrQV9JNZsAhvpTkDImp01xcyTXWPYoNB1CsbzGgFxh"; + checksum = "8Sr087sQcaGw1yOodbRQAOv5bBj_QpxbOWGN75xxKqhRcuAUvdPZ9a009nlXZRwK"; }; // litArgs emits shell syntax diff --git a/internal/rosa/package/tamago.az b/internal/rosa/package/tamago.az index e0aca055..358c52a4 100644 --- a/internal/rosa/package/tamago.az +++ b/internal/rosa/package/tamago.az @@ -3,11 +3,11 @@ package tamago { website = "https://github.com/usbarmory/tamago-go"; anitya = 388872; - version# = "1.27.0"; + version# = "1.27.1"; source = remoteGitHub { suffix = "usbarmory/tamago-go"; tag = "tamago-go"+version; - checksum = "PV8a5fccqZifVUG7HJ6jTFk_0x6YIu1BSgYByCdJoTllm4NuQc9-Ngk_nuS7Nb3i"; + checksum = "TaWPzqqBMuhGslUm9CQvShJRjfONbFEr51mcM5zFlopYhYA6Tb58g8wRPc5a5lQ0"; }; env = [ diff --git a/internal/rosa/package/util-linux/a323dddbcd1ed05a10e7e870b3e1a48b4ed44a43.patch b/internal/rosa/package/util-linux/a323dddbcd1ed05a10e7e870b3e1a48b4ed44a43.patch new file mode 100644 index 00000000..36d6f968 --- /dev/null +++ b/internal/rosa/package/util-linux/a323dddbcd1ed05a10e7e870b3e1a48b4ed44a43.patch @@ -0,0 +1,35 @@ +From a323dddbcd1ed05a10e7e870b3e1a48b4ed44a43 Mon Sep 17 00:00:00 2001 +From: Karel Zak <kzak@redhat.com> +Date: Wed, 2 Sep 2026 13:32:27 +0200 +Subject: [PATCH] libmount: add missing fileutils.h include to hook_idmap.c + +The hook_idmap.c uses RESOLVE_NO_SYMLINKS (added by commit fb8e26535) +but does not include fileutils.h, which provides the fallback #define +for this constant. + +On Fedora (glibc 2.40+), this is masked because glibc's +<bits/fcntl-linux.h> transitively includes <linux/openat2.h>, which +defines RESOLVE_NO_SYMLINKS. On Ubuntu (and other distros with older +glibc), <fcntl.h> does not pull in openat2.h, so the build fails: + + hook_idmap.c:335:33: error: 'RESOLVE_NO_SYMLINKS' undeclared + +Fixes: fb8e26535 ("libmount: pin source path with openat2() for restricted users") +Signed-off-by: Karel Zak <kzak@redhat.com> +(cherry picked from commit 7e2e010874b10b3aabdc3c4c844c9ffc46a4a374) +--- + libmount/src/hook_idmap.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libmount/src/hook_idmap.c b/libmount/src/hook_idmap.c +index 77494e29810..2c697b17154 100644 +--- a/libmount/src/hook_idmap.c ++++ b/libmount/src/hook_idmap.c +@@ -23,6 +23,7 @@ + + #include "strutils.h" + #include "all-io.h" ++#include "fileutils.h" + #include "namespace.h" + + #include "mountP.h" diff --git a/internal/rosa/package/util-linux.az b/internal/rosa/package/util-linux/package.az index 405f2ab7..f065718a 100644 --- a/internal/rosa/package/util-linux.az +++ b/internal/rosa/package/util-linux/package.az @@ -5,7 +5,7 @@ package util-linux { // release candidates confuse Anitya latest = anityaFallback; - version# = "2.42.2"; + version# = "2.42.3"; source = remoteTar { url = "https://www.kernel.org/pub/linux/utils/util-linux/"+ "v"+join { @@ -19,9 +19,10 @@ package util-linux { }; sep = "."; }+"/util-linux-"+version+".tar.gz"; - checksum = "Z4IZapPCKQP37aeWMDhxcvuOCy-IR_aHZvfUEMZ0T7trwC1znZACKH_3ddqXwBCg"; + checksum = "VQ3NCM4kt0-TTrdJElBfckXNCGBv6UfXGTRm1poQ0XP0WBND4eG-O7n5Ly_a-Iw_"; compress = gzip; }; + patches = [ "a323dddbcd1ed05a10e7e870b3e1a48b4ed44a43.patch" ]; bin = [ "bash" ]; exec = make { diff --git a/internal/store/data.go b/internal/store/data.go index 34a0f261..380481a0 100644 --- a/internal/store/data.go +++ b/internal/store/data.go @@ -23,7 +23,8 @@ func entryEncode(w io.Writer, s *hst.State) error { } } -// entryDecodeHeader calls entryReadHeader, returning [hst.AppError] for a non-nil error. +// entryDecodeHeader calls entryReadHeader, returning [hst.AppError] for a +// non-nil error. func entryDecodeHeader(r io.Reader) (hst.Enablements, error) { if et, err := entryReadHeader(r); err != nil { return 0, &hst.AppError{Step: "decode state header", Err: err} @@ -44,11 +45,14 @@ func entryDecode(r io.Reader, p *hst.State) (hst.Enablements, error) { } else if err = p.Config.Validate(hst.VAllowInsecure); err != nil { return et, err } else if p.Enablements.Unwrap() != et { - return et, &hst.AppError{Step: "validate state enablement", Err: os.ErrInvalid, + return et, &hst.AppError{ + Step: "validate state enablement", + Err: os.ErrInvalid, Msg: fmt.Sprintf( "state entry %s has unexpected enablement byte %#x, %#x", p.ID.String(), byte(p.Enablements.Unwrap()), byte(et), - )} + ), + } } else { return et, nil } diff --git a/internal/store/header.go b/internal/store/header.go index 5b5d73be..0e5a92a2 100644 --- a/internal/store/header.go +++ b/internal/store/header.go @@ -60,7 +60,8 @@ func (e *EntrySizeError) Error() string { return "state entry file " + strconv.Quote(e.Name) + " is too short" } -// entryCheckFile checks whether [os.FileInfo] refers to a file that might hold [hst.State]. +// entryCheckFile checks whether [os.FileInfo] refers to a file that might hold +// [hst.State]. func entryCheckFile(fi os.FileInfo) error { if fi.IsDir() { return syscall.EISDIR diff --git a/internal/store/segment.go b/internal/store/segment.go index 06849749..8b7dd0d6 100644 --- a/internal/store/segment.go +++ b/internal/store/segment.go @@ -7,6 +7,7 @@ import ( "os" "strconv" "sync" + "syscall" "hakurei.app/check" "hakurei.app/hst" @@ -76,13 +77,20 @@ func (eh *EntryHandle) save(state *hst.State) error { return err } +// KillFunc is the function signature of syscall.Kill. +type KillFunc func(pid int, sig syscall.Signal) (err error) + // Load loads and validates the state entry header, and returns the // [hst.Enablements] byte. For a non-nil v, the full state payload is decoded -// and stored in the value pointed to by v. +// and stored in the value pointed to by v, and if kill is non-nil, the presence +// of the monitoring process is checked, and a stale entry is destroyed. // // Load validates the embedded [hst.Config] value. A non-nil error returned by // Load is of type [hst.AppError]. -func (eh *EntryHandle) Load(v *hst.State) (hst.Enablements, error) { +func (eh *EntryHandle) Load( + v *hst.State, + kill KillFunc, +) (hst.Enablements, error) { f, err := eh.open(os.O_RDONLY, 0) if err != nil { return 0, err @@ -92,8 +100,41 @@ func (eh *EntryHandle) Load(v *hst.State) (hst.Enablements, error) { if v != nil { et, err = entryDecode(f, v) if err == nil && v.ID != eh.ID { - err = &hst.AppError{Step: "validate state identifier", Err: os.ErrInvalid, - Msg: fmt.Sprintf("state entry %s has unexpected id %s", eh.ID.String(), v.ID.String())} + err = &hst.AppError{ + Step: "validate state identifier", + Err: os.ErrInvalid, + Msg: fmt.Sprintf( + "state entry %s has unexpected id %s", + eh.ID.String(), v.ID.String(), + ), + } + } + if kill != nil { + errno := kill(v.PID, 0) + if errno != nil { + if !errors.Is(errno, syscall.ESRCH) { + err = &hst.AppError{ + Step: "check monitor process", + Err: errno, + } + } else { + if err = eh.Destroy(); err != nil { + err = &hst.AppError{ + Step: "destroy stale entry", + Err: err, + } + } else { + err = &hst.AppError{ + Step: "load stale entry", + Err: errno, + Msg: fmt.Sprintf( + "stale entry %s", + eh.ID.String(), + ), + } + } + } + } } } else { et, err = entryDecodeHeader(f) @@ -127,7 +168,10 @@ type Handle struct { // A non-nil error returned by Lock is of type [hst.AppError]. func (h *Handle) Lock() (unlock func(), err error) { if unlock, err = h.fileMu.Lock(); err != nil { - return nil, &hst.AppError{Step: "acquire lock on store segment " + strconv.Itoa(h.Identity), Err: err} + return nil, &hst.AppError{ + Step: "acquire lock on store segment " + strconv.Itoa(h.Identity), + Err: err, + } } return } @@ -174,8 +218,11 @@ func (h *Handle) Entries() (iter.Seq[*EntryHandle], int, error) { // this should never happen if ent.IsDir() { - eh.DecodeErr = &hst.AppError{Step: step, - Err: errors.New("unexpected directory " + strconv.Quote(ent.Name()) + " in store")} + eh.DecodeErr = &hst.AppError{ + Step: step, + Err: errors.New("unexpected directory " + + strconv.Quote(ent.Name()) + " in store"), + } goto out } @@ -186,7 +233,10 @@ func (h *Handle) Entries() (iter.Seq[*EntryHandle], int, error) { // this either indicates a serious bug or external interference if err := eh.ID.UnmarshalText([]byte(ent.Name())); err != nil { - eh.DecodeErr = &hst.AppError{Step: "decode store segment entry", Err: err} + eh.DecodeErr = &hst.AppError{ + Step: "decode store segment entry", + Err: err, + } goto out } diff --git a/internal/store/segment_test.go b/internal/store/segment_test.go index a244fcdc..6316a278 100644 --- a/internal/store/segment_test.go +++ b/internal/store/segment_test.go @@ -2,6 +2,7 @@ package store_test import ( "errors" + "fmt" "io" "iter" "os" @@ -55,7 +56,7 @@ func TestStateEntryHandle(t *testing.T) { if err := save(&eh, nil); !reflect.DeepEqual(err, wantErr()) { t.Errorf("save: error = %v, want %v", err, wantErr()) } - if _, err := eh.Load(nil); !reflect.DeepEqual(err, wantErr()) { + if _, err := eh.Load(nil, nil); !reflect.DeepEqual(err, wantErr()) { t.Errorf("load: error = %v, want %v", err, wantErr()) } }) @@ -95,8 +96,10 @@ func TestStateEntryHandle(t *testing.T) { t.Run("saveload", func(t *testing.T) { t.Parallel() - eh := store.EntryHandle{Pathname: check.MustAbs(t.TempDir()).Append("entry"), - ID: store.NewTemplateState().ID} + eh := store.EntryHandle{ + Pathname: check.MustAbs(t.TempDir()).Append("entry"), + ID: store.NewTemplateState().ID, + } if err := save(&eh, store.NewTemplateState()); err != nil { t.Fatalf("save: error = %v", err) @@ -125,7 +128,7 @@ func TestStateEntryHandle(t *testing.T) { t.Run("load header only", func(t *testing.T) { t.Parallel() - if et, err := eh.Load(nil); err != nil { + if et, err := eh.Load(nil, nil); err != nil { t.Fatalf("load: error = %v", err) } else if want := store.NewTemplateState().Enablements.Unwrap(); et != want { t.Errorf("load: et = %x, want %x", et, want) @@ -136,7 +139,7 @@ func TestStateEntryHandle(t *testing.T) { t.Parallel() var got hst.State - if _, err := eh.Load(&got); err != nil { + if _, err := eh.Load(&got, nil); err != nil { t.Fatalf("load: error = %v", err) } else if want := store.NewTemplateState(); !reflect.DeepEqual(&got, want) { t.Errorf("load: %#v, want %#v", &got, want) @@ -145,11 +148,64 @@ func TestStateEntryHandle(t *testing.T) { t.Run("load inconsistent", func(t *testing.T) { t.Parallel() - wantErr := &hst.AppError{Step: "validate state identifier", Err: os.ErrInvalid, - Msg: "state entry 00000000000000000000000000000000 has unexpected id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} + wantErr := &hst.AppError{ + Step: "validate state identifier", + Err: os.ErrInvalid, + Msg: "state entry 00000000000000000000000000000000 has unexpected id aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } ehi := store.EntryHandle{Pathname: eh.Pathname} - if _, err := ehi.Load(new(hst.State)); !reflect.DeepEqual(err, wantErr) { + if _, err := ehi.Load(new(hst.State), nil); !reflect.DeepEqual(err, wantErr) { + t.Errorf("load: error = %#v, want %#v", err, wantErr) + } + }) + + t.Run("stale fault", func(t *testing.T) { + t.Parallel() + wantErr := &hst.AppError{ + Step: "check monitor process", + Err: syscall.EFAULT, + } + + if _, err := eh.Load(new(hst.State), func(pid int, sig syscall.Signal) (err error) { + if pid != store.NewTemplateState().PID { + return fmt.Errorf("bad pid %d", pid) + } + if sig != 0 { + return fmt.Errorf("bad signal %d", sig) + } + return syscall.EFAULT + }); !reflect.DeepEqual(err, wantErr) { + t.Errorf("load: error = %#v, want %#v", err, wantErr) + } + }) + + t.Run("stale", func(t *testing.T) { + t.Parallel() + wantErr := &hst.AppError{ + Step: "load stale entry", + Err: syscall.ESRCH, + Msg: "stale entry aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } + + ehi := store.EntryHandle{ + Pathname: check.MustAbs(t.TempDir()).Append("entry"), + ID: eh.ID, + } + + if err := save(&ehi, store.NewTemplateState()); err != nil { + t.Fatalf("save: error = %v", err) + } + + if _, err := ehi.Load(new(hst.State), func(pid int, sig syscall.Signal) (err error) { + if pid != store.NewTemplateState().PID { + return fmt.Errorf("bad pid %d", pid) + } + if sig != 0 { + return fmt.Errorf("bad signal %d", sig) + } + return syscall.ESRCH + }); !reflect.DeepEqual(err, wantErr) { t.Errorf("load: error = %#v, want %#v", err, wantErr) } }) @@ -229,7 +285,9 @@ func TestSegmentHandle(t *testing.T) { } } - slices.SortFunc(got, func(a, b *store.EntryHandle) int { return strings.Compare(a.Pathname.String(), b.Pathname.String()) }) + slices.SortFunc(got, func(a, b *store.EntryHandle) int { + return strings.Compare(a.Pathname.String(), b.Pathname.String()) + }) want := tc.want(func(err error, name string) *store.EntryHandle { eh := store.EntryHandle{DecodeErr: err, Pathname: segment.Append(name)} if err == nil { @@ -247,6 +305,8 @@ func TestSegmentHandle(t *testing.T) { } t.Run("nonexistent", func(t *testing.T) { + t.Parallel() + var wantErr = &hst.AppError{Step: "read store segment entries", Err: &os.PathError{ Op: "open", Path: "/proc/nonexistent", diff --git a/internal/store/store.go b/internal/store/store.go index 3604521c..eb9cec41 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -1,4 +1,5 @@ -// Package store implements cross-process state tracking for hakurei container instances. +// Package store provides storage for hakurei instance states, safe for +// concurrent and cross-process use. package store import ( @@ -15,11 +16,15 @@ import ( "hakurei.app/internal/lockedfile" ) -// MutexName is the pathname of the file backing [lockedfile.Mutex] of a [Store] and [Handle]. +// MutexName is the pathname of the file backing [lockedfile.Mutex] of a [Store] +// and [Handle]. const MutexName = "lock" -// A Store keeps track of [hst.State] via a well-known filesystem accessible to all hakurei priv-side processes. -// Access to store data and related resources are synchronised on a per-segment basis via [Handle]. +// A Store keeps track of [hst.State] via a well-known filesystem accessible to +// all hakurei priv-side processes. +// +// Access to store data and related resources are synchronised on a per-segment +// basis via [Handle]. type Store struct { // Pathname of directory that the store is rooted in. base *check.Absolute @@ -28,8 +33,10 @@ type Store struct { handles sync.Map // Inter-process mutex to synchronise operations against the entire store. - // Held during List and when initialising previously unknown identities during Do. - // Must not be accessed directly. Callers should use the bigLock method instead. + // + // Held during List and when initialising previously unknown identities + // during Do. Must not be accessed directly. Callers should use the bigLock + // method instead. fileMu *lockedfile.Mutex // For creating the base directory. @@ -43,11 +50,17 @@ type Store struct { func (s *Store) bigLock() (unlock func(), err error) { s.mkdirOnce.Do(func() { s.mkdirErr = os.MkdirAll(s.base.String(), 0700) }) if s.mkdirErr != nil { - return nil, &hst.AppError{Step: "create state store directory", Err: s.mkdirErr} + return nil, &hst.AppError{ + Step: "create state store directory", + Err: s.mkdirErr, + } } if unlock, err = s.fileMu.Lock(); err != nil { - return nil, &hst.AppError{Step: "acquire lock on the state store", Err: err} + return nil, &hst.AppError{ + Step: "acquire lock on the state store", + Err: err, + } } return } @@ -73,7 +86,10 @@ func (s *Store) Handle(identity int) (*Handle, error) { if err != nil && !errors.Is(err, fs.ErrExist) { // handle methods will likely return ENOENT s.handles.CompareAndDelete(identity, h) - return nil, &hst.AppError{Step: "create store segment directory", Err: err} + return nil, &hst.AppError{ + Step: "create store segment directory", + Err: err, + } } } return h, nil @@ -88,8 +104,9 @@ type SegmentIdentity struct { } // Segments returns an iterator over all [SegmentIdentity] known to the [Store]. -// To obtain a [Handle] on a segment, caller must then call [Store.Handle]. -// A non-nil error returned by segments is of type [hst.AppError]. +// +// To obtain a [Handle] on a segment, caller must then call [Store.Handle]. A +// non-nil error returned by segments is of type [hst.AppError]. func (s *Store) Segments() (iter.Seq[SegmentIdentity], int, error) { // read directory contents, should only contain storeMutexName and identity var entries []os.DirEntry @@ -102,7 +119,10 @@ func (s *Store) Segments() (iter.Seq[SegmentIdentity], int, error) { unlock() if err != nil && !errors.Is(err, os.ErrNotExist) { - return nil, -1, &hst.AppError{Step: "read store segments", Err: err} + return nil, -1, &hst.AppError{ + Step: "read store segments", + Err: err, + } } } @@ -126,19 +146,25 @@ func (s *Store) Segments() (iter.Seq[SegmentIdentity], int, error) { } // this should never happen - si.Err = &hst.AppError{Step: step, Err: syscall.ENOTDIR, - Msg: "skipped non-directory entry " + strconv.Quote(ent.Name())} + si.Err = &hst.AppError{ + Step: step, Err: syscall.ENOTDIR, + Msg: "skipped non-directory entry " + strconv.Quote(ent.Name()), + } goto out } // failure paths either indicates a serious bug or external interference if v, err := strconv.Atoi(ent.Name()); err != nil { - si.Err = &hst.AppError{Step: step, Err: err, - Msg: "skipped non-identity entry " + strconv.Quote(ent.Name())} + si.Err = &hst.AppError{ + Step: step, Err: err, + Msg: "skipped non-identity entry " + strconv.Quote(ent.Name()), + } goto out } else if v < hst.IdentityStart || v > hst.IdentityEnd { - si.Err = &hst.AppError{Step: step, Err: syscall.ERANGE, - Msg: "skipped out of bounds entry " + strconv.Itoa(v)} + si.Err = &hst.AppError{ + Step: step, Err: syscall.ERANGE, + Msg: "skipped out of bounds entry " + strconv.Itoa(v), + } goto out } else { si.Identity = v @@ -152,9 +178,12 @@ func (s *Store) Segments() (iter.Seq[SegmentIdentity], int, error) { }, l, nil } -// All returns a non-reusable iterator over all [EntryHandle] known to this [Store]. -// Callers must call copyError after completing iteration and handle the error accordingly. -// A non-nil error returned by copyError is of type [hst.AppError]. +// All returns a non-reusable iterator over all [EntryHandle] known to this +// [Store]. The resulting handles may be retained, but are only safe to use +// during the iteration producing them. +// +// Callers must call copyError after completing iteration and handle the error +// accordingly. A non-nil error returned by copyError is of type [hst.AppError]. func (s *Store) All() (entries iter.Seq[*EntryHandle], copyError func() error) { var savedErr error return func(yield func(*EntryHandle) bool) { @@ -197,7 +226,12 @@ func (s *Store) All() (entries iter.Seq[*EntryHandle], copyError func() error) { } // New returns the address of a new instance of [Store]. -// Multiple instances of [Store] rooted in the same directory is possible, but unsupported. +// +// Multiple instances of [Store] rooted in the same directory is possible, but +// unsupported. func New(base *check.Absolute) *Store { - return &Store{base: base, fileMu: lockedfile.MutexAt(base.Append(MutexName).String())} + return &Store{ + base: base, + fileMu: lockedfile.MutexAt(base.Append(MutexName).String()), + } } |
