cmd/app: enforce mutable instance exclusion
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m47s
Test / ShareFS (push) Successful in 3m46s
Test / Hakurei (push) Successful in 3m57s
Test / Sandbox (race detector) (push) Successful in 5m31s
Test / Hakurei (race detector) (push) Successful in 6m39s
Test / Flake checks (push) Successful in 1m7s
Test / Create distribution (push) Successful in 55s
Test / Sandbox (push) Successful in 2m47s
Test / ShareFS (push) Successful in 3m46s
Test / Hakurei (push) Successful in 3m57s
Test / Sandbox (race detector) (push) Successful in 5m31s
Test / Hakurei (race detector) (push) Successful in 6m39s
Test / Flake checks (push) Successful in 1m7s
This avoids invoking undefined behaviour in the underlying overlay filesystem implementation. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
+9
-1
@@ -31,7 +31,12 @@ func parsePair(s string) (source, target *check.Absolute, err error) {
|
||||
|
||||
// parse decodes a high-level configuration stream and returns its
|
||||
// corresponding [hst.Config].
|
||||
func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
|
||||
func parse(
|
||||
id string,
|
||||
base *check.Absolute,
|
||||
r io.Reader,
|
||||
templateP *string,
|
||||
) (*hst.Config, error) {
|
||||
shell := fhs.AbsRoot.Append("bin", "zsh")
|
||||
home := hst.AbsPrivateTmp.Append("home")
|
||||
|
||||
@@ -99,6 +104,9 @@ func parse(id string, base *check.Absolute, r io.Reader) (*hst.Config, error) {
|
||||
} else if v, err := strconv.Atoi(identity); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if templateP != nil {
|
||||
*templateP = template
|
||||
}
|
||||
c.Identity = v
|
||||
root.Upper = base.Append("template", template)
|
||||
}
|
||||
|
||||
@@ -130,6 +130,7 @@ talk com.canonical.Unity
|
||||
tc.name,
|
||||
base,
|
||||
strings.NewReader(tc.data),
|
||||
nil,
|
||||
)
|
||||
|
||||
if !reflect.DeepEqual(err, tc.err) {
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"hakurei.app/check"
|
||||
"hakurei.app/fhs"
|
||||
"hakurei.app/hst"
|
||||
"hakurei.app/internal/env"
|
||||
"hakurei.app/internal/lockedfile"
|
||||
"hakurei.app/internal/outcome"
|
||||
)
|
||||
|
||||
// MutationConflictError describes an active mutable instance.
|
||||
type MutationConflictError string
|
||||
|
||||
func (e MutationConflictError) Error() string {
|
||||
return "mutable instance active at " + string(e)
|
||||
}
|
||||
|
||||
// informTemplate guards intention of a template or its derivatives.
|
||||
func informTemplate(base *check.Absolute, name string, mutable bool) (func() error, error) {
|
||||
mu := lockedfile.MutexAt(base.Append("lock", name).String())
|
||||
if unlock, err := mu.Lock(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
defer unlock()
|
||||
}
|
||||
|
||||
marker := base.Append("lock", "."+name)
|
||||
if p, err := os.ReadFile(marker.String()); err == nil {
|
||||
if _, err = os.Stat(fhs.AbsProc.Append(string(p)).String()); err == nil {
|
||||
return nil, MutationConflictError(p)
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
log.Printf("removing stale marker by %s", string(p))
|
||||
if err = os.Remove(marker.String()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if !errors.Is(err, os.ErrNotExist) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !mutable {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var active []hst.ID
|
||||
var sc hst.Paths
|
||||
env.CopyPaths().Copy(&sc, new(outcome.Hsu).MustID(nil))
|
||||
entries, copyError := outcome.NewStore(&sc).All()
|
||||
var s hst.State
|
||||
for eh := range entries {
|
||||
if _, err := eh.Load(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if s.Validate(0) != nil || len(s.Container.Filesystem) < 1 {
|
||||
continue
|
||||
}
|
||||
root, ok := s.Container.Filesystem[0].FilesystemConfig.(*hst.FSOverlay)
|
||||
if !ok || root == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !root.Target.Is(fhs.AbsRoot) ||
|
||||
len(root.Lower) != 1 ||
|
||||
!root.Lower[0].Is(base.Append("initial")) ||
|
||||
!root.Upper.Is(base.Append("template", name)) ||
|
||||
root.Work != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
active = append(active, s.ID)
|
||||
}
|
||||
if err := copyError(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(active) != 0 {
|
||||
var buf strings.Builder
|
||||
buf.WriteString("derivative instances still active:")
|
||||
for _, id := range active {
|
||||
buf.WriteString("\n\t")
|
||||
buf.WriteString(id.String())
|
||||
}
|
||||
return nil, errors.New(buf.String())
|
||||
}
|
||||
|
||||
return func() error { return os.RemoveAll(marker.String()) }, os.WriteFile(
|
||||
marker.String(),
|
||||
[]byte(strconv.Itoa(os.Getpid())),
|
||||
0400,
|
||||
)
|
||||
}
|
||||
|
||||
// acquireTemplate obtains exclusivity of a template.
|
||||
func acquireTemplate(base *check.Absolute, name string) (remove func() error, err error) {
|
||||
return informTemplate(base, name, true)
|
||||
}
|
||||
|
||||
// enterTemplate checks against exclusivity of a template.
|
||||
func enterTemplate(base *check.Absolute, name string) error {
|
||||
_, err := informTemplate(base, name, false)
|
||||
return err
|
||||
}
|
||||
+11
-2
@@ -128,7 +128,12 @@ func main() {
|
||||
config.Container.Home = a
|
||||
}
|
||||
|
||||
return run(ctx, msg, &config)
|
||||
remove, err := acquireTemplate(base, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = run(ctx, msg, &config)
|
||||
return errors.Join(err, remove())
|
||||
},
|
||||
).Flag(
|
||||
&flagShell,
|
||||
@@ -180,7 +185,8 @@ func main() {
|
||||
r = io.MultiReader(f, common)
|
||||
}
|
||||
|
||||
config, err = parse(args[0], base, r)
|
||||
var name string
|
||||
config, err = parse(args[0], base, r, &name)
|
||||
if closeErr := f.Close(); err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
@@ -197,6 +203,9 @@ func main() {
|
||||
config.Container.Args[2] = flagCommand
|
||||
}
|
||||
|
||||
if err = enterTemplate(base, name); err != nil {
|
||||
return err
|
||||
}
|
||||
return run(ctx, msg, config, args[1:]...)
|
||||
},
|
||||
).
|
||||
|
||||
Reference in New Issue
Block a user