package host import ( "github.com/carlosframework/platform/internal/rootstate" "golang.org/x/sys/unix" "os" "testing" ) func TestPlan14PersistentChrootMountBoundary(t *testing.T) { if !plan14RootFixture(t) { return } for _, p := range []string{"/data", "/view"} { if e := os.Mkdir(p, 0755); e != nil { t.Fatal(e) } } if e := unix.Mount("/data", "/view", "", unix.MS_BIND, ""); e != nil { t.Fatal(e) } defer unix.Unmount("/view", 0) f, e := os.Open("/view") if e != nil { t.Fatal(e) } defer f.Close() before, e := rootstate.ObjectIdentity(f) if e != nil || before.MountLineage == "" { t.Fatal(before, e) } if e := unix.Mount("/view", "/view", "", unix.MS_BIND, ""); e != nil { t.Fatal(e) } second, e := os.Open("/view") if e != nil { t.Fatal(e) } after, e := rootstate.ObjectIdentity(second) second.Close() unmount := unix.Unmount("/view", 0) if e != nil || unmount != nil || before.Same(after) { t.Fatal("substituted mount", e, unmount, before, after) } restored, e := rootstate.ObjectIdentity(f) if e != nil || !before.Same(restored) { t.Fatal("restored mount", e) } if err := unix.Unmount("/view", unix.MNT_DETACH); err != nil { t.Fatal(err) } if err := unix.Mount("/data", "/view", "", unix.MS_BIND, ""); err != nil { t.Fatal(err) } replacement, err := os.Open("/view") if err != nil { t.Fatal(err) } replaced, err := rootstate.ObjectIdentity(replacement) replacement.Close() if err != nil || replaced.MountUnique == before.MountUnique || !replaced.SameMountNamespace(before) || replaced.MountLineage != before.MountLineage || replaced.Filesystem != before.Filesystem || replaced.Handle != before.Handle || replaced.Inode != before.Inode { t.Fatal("replacement did not preserve geometry and persistent object", before, replaced, err) } if before.Same(replaced) { t.Fatal("replacement mount with unchanged geometry accepted") } replaced.Mount = before.Mount if before.Same(replaced) { t.Fatal("replacement mount accepted with a recycled legacy number") } for _, path := range []string{"/source", "/parent", "/parent/view"} { if err := os.Mkdir(path, 0755); err != nil { t.Fatal(err) } } if err := unix.Mount("/source", "/parent/view", "", unix.MS_BIND, ""); err != nil { t.Fatal(err) } defer unix.Unmount("/parent/view", 0) capture := func() rootstate.FileIdentity { f, err := os.Open("/parent/view") if err != nil { t.Fatal(err) } defer f.Close() id, err := rootstate.ObjectIdentity(f) if err != nil { t.Fatal(err) } return id } child := capture() if err := unix.Mount("/parent", "/parent", "", unix.MS_BIND|unix.MS_REC, ""); err != nil { t.Fatal(err) } defer unix.Unmount("/parent", unix.MNT_DETACH) rebound := capture() if child.MountRoot != rebound.MountRoot || child.MountPoint != rebound.MountPoint || child.Filesystem != rebound.Filesystem || child.Handle != rebound.Handle || child.Inode != rebound.Inode { t.Fatal("recursive bind changed the child fixture", child, rebound) } if child.Same(rebound) || rebound.Same(child) { t.Fatal("recursive ancestor substitution accepted") } }