84 lines
3.0 KiB
Go
84 lines
3.0 KiB
Go
//go:build linux
|
|
|
|
package apply
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/declaration"
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// The replay of novox/hq issue 358, written only with what the node-engine had before its fix, so it can be laid
|
|
// over the commit before (mesh-lab's replays register): a mesh directory outside every home was given its mode
|
|
// and owner by path, so a parent swapped for a link between two applies carried root's chmod to the link's target.
|
|
|
|
// aMeshDirectory applies a directory declared at <base>/parent/state once, as the mesh's own, and answers it
|
|
// with the record a second apply would read.
|
|
func aMeshDirectory(t *testing.T) (base string, r *declaration.Directory, previous store.Applied) {
|
|
t.Helper()
|
|
base = t.TempDir()
|
|
r = &declaration.Directory{ID: "mod.state", Path: filepath.Join(base, "parent", "state"), Mode: "0700"}
|
|
out, err := applyDirectory(r, store.Applied{}, false)
|
|
if err != nil {
|
|
t.Fatalf("the first apply: %v", err)
|
|
}
|
|
if out.Action != "created" {
|
|
t.Fatalf("the first apply said %q, want created", out.Action)
|
|
}
|
|
return base, r, store.Applied{ID: r.ID, Type: "directory", Target: r.Path}
|
|
}
|
|
|
|
// swapParentForLink moves <base>/parent away and puts a link to elsewhere (as written) in its place, where
|
|
// elsewhere holds a directory state of mode 0755: what an account that can write <base> does between two applies.
|
|
func swapParentForLink(t *testing.T, base, to string) (target string) {
|
|
t.Helper()
|
|
elsewhere := filepath.Join(base, "elsewhere")
|
|
target = filepath.Join(elsewhere, "state")
|
|
if err := os.MkdirAll(target, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Chmod(target, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Rename(filepath.Join(base, "parent"), filepath.Join(base, "moved")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.Symlink(to, filepath.Join(base, "parent")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return target
|
|
}
|
|
|
|
func permOf(t *testing.T, path string) os.FileMode {
|
|
t.Helper()
|
|
info, err := os.Lstat(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return info.Mode().Perm()
|
|
}
|
|
|
|
// The acceptance of issue 358: a parent swapped for a link after an apply leaves the link's target untouched, and
|
|
// the refusal names the link and who could have made it.
|
|
func TestAParentSwappedForALinkLeavesItsTargetUntouched(t *testing.T) {
|
|
base, r, previous := aMeshDirectory(t)
|
|
target := swapParentForLink(t, base, filepath.Join(base, "elsewhere"))
|
|
|
|
_, err := applyDirectory(r, previous, true)
|
|
var refusal *PlacementRefusedError
|
|
if !errors.As(err, &refusal) {
|
|
t.Fatalf("the directory reached through a link was not refused: %v", err)
|
|
}
|
|
if !strings.Contains(err.Error(), filepath.Join(base, "parent")) || !strings.Contains(err.Error(), "could replace") {
|
|
t.Errorf("the refusal does not name the link and who could replace it: %v", err)
|
|
}
|
|
if got := permOf(t, target); got != 0o755 {
|
|
t.Errorf("the link's target was given mode %o (was 755): root's chmod followed the link", got)
|
|
}
|
|
}
|