container/check: relocate overlay escape
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m12s
Test / Hakurei (push) Successful in 3m8s
Test / Hpkg (push) Successful in 4m9s
Test / Sandbox (race detector) (push) Successful in 4m31s
Test / Hakurei (race detector) (push) Successful in 5m25s
Test / Flake checks (push) Successful in 1m40s
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m12s
Test / Hakurei (push) Successful in 3m8s
Test / Hpkg (push) Successful in 4m9s
Test / Sandbox (race detector) (push) Successful in 4m31s
Test / Hakurei (race detector) (push) Successful in 5m25s
Test / Flake checks (push) Successful in 1m40s
This is used in hst to format strings. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
parent
584ce3da68
commit
1f0226f7e0
29
container/check/overlay.go
Normal file
29
container/check/overlay.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package check
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SpecialOverlayEscape is the escape string for overlay mount options.
|
||||||
|
SpecialOverlayEscape = `\`
|
||||||
|
// SpecialOverlayOption is the separator string between overlay mount options.
|
||||||
|
SpecialOverlayOption = ","
|
||||||
|
// SpecialOverlayPath is the separator string between overlay paths.
|
||||||
|
SpecialOverlayPath = ":"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EscapeOverlayDataSegment escapes a string for formatting into the data argument of an overlay mount call.
|
||||||
|
func EscapeOverlayDataSegment(s string) string {
|
||||||
|
if s == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if f := strings.SplitN(s, "\x00", 2); len(f) > 0 {
|
||||||
|
s = f[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.NewReplacer(
|
||||||
|
SpecialOverlayEscape, SpecialOverlayEscape+SpecialOverlayEscape,
|
||||||
|
SpecialOverlayOption, SpecialOverlayEscape+SpecialOverlayOption,
|
||||||
|
SpecialOverlayPath, SpecialOverlayEscape+SpecialOverlayPath,
|
||||||
|
).Replace(s)
|
||||||
|
}
|
27
container/check/overlay_test.go
Normal file
27
container/check/overlay_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package check_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEscapeOverlayDataSegment(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
s string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"zero", "", ""},
|
||||||
|
{"multi", `\\\:,:,\\\`, `\\\\\\\:\,\:\,\\\\\\`},
|
||||||
|
{"bwrap", `/path :,\`, `/path \:\,\\`},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
if got := check.EscapeOverlayDataSegment(tc.s); got != tc.want {
|
||||||
|
t.Errorf("escapeOverlayDataSegment: %s, want %s", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -139,7 +139,7 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error {
|
|||||||
if v, err := k.evalSymlinks(o.Upper.String()); err != nil {
|
if v, err := k.evalSymlinks(o.Upper.String()); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
o.upper = EscapeOverlayDataSegment(toHost(v))
|
o.upper = check.EscapeOverlayDataSegment(toHost(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +147,7 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error {
|
|||||||
if v, err := k.evalSymlinks(o.Work.String()); err != nil {
|
if v, err := k.evalSymlinks(o.Work.String()); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
o.work = EscapeOverlayDataSegment(toHost(v))
|
o.work = check.EscapeOverlayDataSegment(toHost(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,7 +157,7 @@ func (o *MountOverlayOp) early(_ *setupState, k syscallDispatcher) error {
|
|||||||
if v, err := k.evalSymlinks(a.String()); err != nil {
|
if v, err := k.evalSymlinks(a.String()); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
o.lower[i] = EscapeOverlayDataSegment(toHost(v))
|
o.lower[i] = check.EscapeOverlayDataSegment(toHost(v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -199,10 +199,10 @@ func (o *MountOverlayOp) apply(state *setupState, k syscallDispatcher) error {
|
|||||||
OptionOverlayWorkdir+"="+o.work)
|
OptionOverlayWorkdir+"="+o.work)
|
||||||
}
|
}
|
||||||
options = append(options,
|
options = append(options,
|
||||||
OptionOverlayLowerdir+"="+strings.Join(o.lower, SpecialOverlayPath),
|
OptionOverlayLowerdir+"="+strings.Join(o.lower, check.SpecialOverlayPath),
|
||||||
OptionOverlayUserxattr)
|
OptionOverlayUserxattr)
|
||||||
|
|
||||||
return k.mount(SourceOverlay, target, FstypeOverlay, 0, strings.Join(options, SpecialOverlayOption))
|
return k.mount(SourceOverlay, target, FstypeOverlay, 0, strings.Join(options, check.SpecialOverlayOption))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *MountOverlayOp) Is(op Op) bool {
|
func (o *MountOverlayOp) Is(op Op) bool {
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
. "syscall"
|
. "syscall"
|
||||||
|
|
||||||
"hakurei.app/container/vfs"
|
"hakurei.app/container/vfs"
|
||||||
@ -85,13 +84,6 @@ const (
|
|||||||
// OptionOverlayUserxattr represents the userxattr option of the overlay pseudo-filesystem.
|
// OptionOverlayUserxattr represents the userxattr option of the overlay pseudo-filesystem.
|
||||||
// Use the "user.overlay." xattr namespace instead of "trusted.overlay.".
|
// Use the "user.overlay." xattr namespace instead of "trusted.overlay.".
|
||||||
OptionOverlayUserxattr = "userxattr"
|
OptionOverlayUserxattr = "userxattr"
|
||||||
|
|
||||||
// SpecialOverlayEscape is the escape string for overlay mount options.
|
|
||||||
SpecialOverlayEscape = `\`
|
|
||||||
// SpecialOverlayOption is the separator string between overlay mount options.
|
|
||||||
SpecialOverlayOption = ","
|
|
||||||
// SpecialOverlayPath is the separator string between overlay paths.
|
|
||||||
SpecialOverlayPath = ":"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
|
// bindMount mounts source on target and recursively applies flags if MS_REC is set.
|
||||||
@ -206,20 +198,3 @@ func parentPerm(perm os.FileMode) os.FileMode {
|
|||||||
}
|
}
|
||||||
return os.FileMode(pperm)
|
return os.FileMode(pperm)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EscapeOverlayDataSegment escapes a string for formatting into the data argument of an overlay mount call.
|
|
||||||
func EscapeOverlayDataSegment(s string) string {
|
|
||||||
if s == zeroString {
|
|
||||||
return zeroString
|
|
||||||
}
|
|
||||||
|
|
||||||
if f := strings.SplitN(s, "\x00", 2); len(f) > 0 {
|
|
||||||
s = f[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return strings.NewReplacer(
|
|
||||||
SpecialOverlayEscape, SpecialOverlayEscape+SpecialOverlayEscape,
|
|
||||||
SpecialOverlayOption, SpecialOverlayEscape+SpecialOverlayOption,
|
|
||||||
SpecialOverlayPath, SpecialOverlayEscape+SpecialOverlayPath,
|
|
||||||
).Replace(s)
|
|
||||||
}
|
|
||||||
|
@ -281,23 +281,3 @@ func TestParentPerm(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEscapeOverlayDataSegment(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
s string
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{"zero", zeroString, zeroString},
|
|
||||||
{"multi", `\\\:,:,\\\`, `\\\\\\\:\,\:\,\\\\\\`},
|
|
||||||
{"bwrap", `/path :,\`, `/path \:\,\\`},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
if got := EscapeOverlayDataSegment(tc.s); got != tc.want {
|
|
||||||
t.Errorf("escapeOverlayDataSegment: %s, want %s", got, tc.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"hakurei.app/container/check"
|
||||||
"hakurei.app/container/vfs"
|
"hakurei.app/container/vfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,8 +50,8 @@ func TestToHost(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InternalToHostOvlEscape exports toHost passed to EscapeOverlayDataSegment.
|
// InternalToHostOvlEscape exports toHost passed to [check.EscapeOverlayDataSegment].
|
||||||
func InternalToHostOvlEscape(s string) string { return EscapeOverlayDataSegment(toHost(s)) }
|
func InternalToHostOvlEscape(s string) string { return check.EscapeOverlayDataSegment(toHost(s)) }
|
||||||
|
|
||||||
func TestCreateFile(t *testing.T) {
|
func TestCreateFile(t *testing.T) {
|
||||||
t.Run("nonexistent", func(t *testing.T) {
|
t.Run("nonexistent", func(t *testing.T) {
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"hakurei.app/container"
|
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,18 +81,18 @@ func (o *FSOverlay) String() string {
|
|||||||
|
|
||||||
lower := make([]string, len(o.Lower))
|
lower := make([]string, len(o.Lower))
|
||||||
for i, a := range o.Lower {
|
for i, a := range o.Lower {
|
||||||
lower[i] = container.EscapeOverlayDataSegment(a.String())
|
lower[i] = check.EscapeOverlayDataSegment(a.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.Upper != nil && o.Work != nil {
|
if o.Upper != nil && o.Work != nil {
|
||||||
return "w*" + strings.Join(append([]string{
|
return "w*" + strings.Join(append([]string{
|
||||||
container.EscapeOverlayDataSegment(o.Target.String()),
|
check.EscapeOverlayDataSegment(o.Target.String()),
|
||||||
container.EscapeOverlayDataSegment(o.Upper.String()),
|
check.EscapeOverlayDataSegment(o.Upper.String()),
|
||||||
container.EscapeOverlayDataSegment(o.Work.String())},
|
check.EscapeOverlayDataSegment(o.Work.String())},
|
||||||
lower...), container.SpecialOverlayPath)
|
lower...), check.SpecialOverlayPath)
|
||||||
} else {
|
} else {
|
||||||
return "*" + strings.Join(append([]string{
|
return "*" + strings.Join(append([]string{
|
||||||
container.EscapeOverlayDataSegment(o.Target.String())},
|
check.EscapeOverlayDataSegment(o.Target.String())},
|
||||||
lower...), container.SpecialOverlayPath)
|
lower...), check.SpecialOverlayPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user