forked from rosa/hakurei
218f7fa345
This avoids invoking undefined behaviour in the underlying overlay filesystem implementation. Signed-off-by: Ophestra <cat@gensokyo.uk>
112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
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
|
|
}
|