From 905b9f9785263297611a61facc8b6bdf4c3e257f Mon Sep 17 00:00:00 2001 From: Ophestra Date: Fri, 29 Aug 2025 02:56:56 +0900 Subject: [PATCH] container/initoverlay: invalid argument type This eliminates generic WrapErr from overlay. Signed-off-by: Ophestra --- container/initoverlay.go | 40 +++++++++++++++++++++++++++++++---- container/initoverlay_test.go | 34 +++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/container/initoverlay.go b/container/initoverlay.go index f9daaaa..c3f1c59 100644 --- a/container/initoverlay.go +++ b/container/initoverlay.go @@ -3,7 +3,6 @@ package container import ( "encoding/gob" "fmt" - "io/fs" "slices" "strings" ) @@ -19,6 +18,39 @@ const ( func init() { gob.Register(new(MountOverlayOp)) } +const ( + // OverlayEphemeralUnexpectedUpper is set when [MountOverlayOp.Work] is nil + // and [MountOverlayOp.Upper] holds an unexpected value. + OverlayEphemeralUnexpectedUpper = iota + // OverlayReadonlyLower is set when [MountOverlayOp.Lower] contains less than + // two entries when mounting readonly. + OverlayReadonlyLower + // OverlayEmptyLower is set when [MountOverlayOp.Lower] has length of zero. + OverlayEmptyLower +) + +// OverlayArgumentError is returned for [MountOverlayOp] supplied with invalid argument. +type OverlayArgumentError struct { + Type uintptr + Value string +} + +func (e *OverlayArgumentError) Error() string { + switch e.Type { + case OverlayEphemeralUnexpectedUpper: + return fmt.Sprintf("upperdir has unexpected value %q", e.Value) + + case OverlayReadonlyLower: + return "readonly overlay requires at least two lowerdir" + + case OverlayEmptyLower: + return "overlay requires at least one lowerdir" + + default: + return fmt.Sprintf("invalid overlay argument error %#x", e.Type) + } +} + // Overlay appends an [Op] that mounts the overlay pseudo filesystem on [MountOverlayOp.Target]. func (f *Ops) Overlay(target, state, work *Absolute, layers ...*Absolute) *Ops { *f = append(*f, &MountOverlayOp{ @@ -89,7 +121,7 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error { o.ephemeral = true // intermediate root not yet available default: - return msg.WrapErr(fs.ErrInvalid, fmt.Sprintf("upperdir has unexpected value %q", o.Upper)) + return &OverlayArgumentError{OverlayEphemeralUnexpectedUpper, o.Upper.String()} } } // readonly handled in apply @@ -152,12 +184,12 @@ func (o *MountOverlayOp) apply(state *setupState, k syscallDispatcher) error { if o.upper == zeroString && o.work == zeroString { // readonly if len(o.Lower) < 2 { - return msg.WrapErr(fs.ErrInvalid, "readonly overlay requires at least two lowerdir") + return &OverlayArgumentError{OverlayReadonlyLower, zeroString} } // "upperdir=" and "workdir=" may be omitted. In that case the overlay will be read-only } else { if len(o.Lower) == 0 { - return msg.WrapErr(fs.ErrInvalid, "overlay requires at least one lowerdir") + return &OverlayArgumentError{OverlayEmptyLower, zeroString} } options = append(options, OptionOverlayUpperdir+"="+o.upper, diff --git a/container/initoverlay_test.go b/container/initoverlay_test.go index 64b0bf3..9dae211 100644 --- a/container/initoverlay_test.go +++ b/container/initoverlay_test.go @@ -2,12 +2,38 @@ package container import ( "errors" - "io/fs" "os" "testing" ) func TestMountOverlayOp(t *testing.T) { + t.Run("argument error", func(t *testing.T) { + testCases := []struct { + name string + err *OverlayArgumentError + want string + }{ + {"unexpected upper", &OverlayArgumentError{OverlayEphemeralUnexpectedUpper, "/proc/"}, + `upperdir has unexpected value "/proc/"`}, + + {"lower ro short", &OverlayArgumentError{OverlayReadonlyLower, zeroString}, + "readonly overlay requires at least two lowerdir"}, + + {"lower short", &OverlayArgumentError{OverlayEmptyLower, zeroString}, + "overlay requires at least one lowerdir"}, + + {"oob", &OverlayArgumentError{0xdeadbeef, zeroString}, + "invalid overlay argument error 0xdeadbeef"}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if got := tc.err.Error(); got != tc.want { + t.Errorf("Error: %q, want %q", got, tc.want) + } + }) + } + }) + checkOpBehaviour(t, []opBehaviourTestCase{ {"mkdirTemp invalid ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ Target: MustAbs("/"), @@ -16,7 +42,7 @@ func TestMountOverlayOp(t *testing.T) { MustAbs("/var/lib/planterette/app/org.chromium.Chromium@debian:f92c9052"), }, Upper: MustAbs("/proc/"), - }, nil, msg.WrapErr(fs.ErrInvalid, `upperdir has unexpected value "/proc/"`), nil, nil}, + }, nil, &OverlayArgumentError{OverlayEphemeralUnexpectedUpper, "/proc/"}, nil, nil}, {"mkdirTemp upper ephemeral", &Params{ParentPerm: 0705}, &MountOverlayOp{ Target: MustAbs("/"), @@ -81,7 +107,7 @@ func TestMountOverlayOp(t *testing.T) { {"evalSymlinks", expectArgs{"/mnt-root/nix/.ro-store"}, "/mnt-root/nix/.ro-store", nil}, }, nil, []kexpect{ {"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0755)}, nil, nil}, - }, msg.WrapErr(fs.ErrInvalid, "readonly overlay requires at least two lowerdir")}, + }, &OverlayArgumentError{OverlayReadonlyLower, zeroString}}, {"success ro noPrefix", &Params{ParentPerm: 0755}, &MountOverlayOp{ Target: MustAbs("/nix/store"), @@ -129,7 +155,7 @@ func TestMountOverlayOp(t *testing.T) { {"evalSymlinks", expectArgs{"/mnt-root/nix/.rw-store/work"}, "/mnt-root/nix/.rw-store/.work", nil}, }, nil, []kexpect{ {"mkdirAll", expectArgs{"/sysroot/nix/store", os.FileMode(0700)}, nil, nil}, - }, msg.WrapErr(fs.ErrInvalid, "overlay requires at least one lowerdir")}, + }, &OverlayArgumentError{OverlayEmptyLower, zeroString}}, {"evalSymlinks upper", &Params{ParentPerm: 0700}, &MountOverlayOp{ Target: MustAbs("/nix/store"),