diff --git a/sandbox/container_test.go b/sandbox/container_test.go index 41fd849..c8bdcd0 100644 --- a/sandbox/container_test.go +++ b/sandbox/container_test.go @@ -88,7 +88,8 @@ func TestContainer(t *testing.T) { Tmpfs("/tmp", 0, 0755). Bind(os.Args[0], os.Args[0], 0). Mkdir("/usr/bin"). - Link(os.Args[0], "/usr/bin/sandbox.test") + Link(os.Args[0], "/usr/bin/sandbox.test"). + Place("/etc/hostname", []byte(container.Args[5])) // in case test has cgo enabled var libPaths []string if entries, err := ldd.ExecFilter(ctx, @@ -109,7 +110,9 @@ func TestContainer(t *testing.T) { mnt = append(mnt, tc.mnt...) mnt = append(mnt, &check.Mntent{FSName: "tmpfs", Dir: "/tmp", Type: "tmpfs", Opts: "host_passthrough"}, - &check.Mntent{FSName: "\x00", Dir: os.Args[0], Type: "\x00", Opts: "\x00"}) + &check.Mntent{FSName: "\x00", Dir: os.Args[0], Type: "\x00", Opts: "\x00"}, + &check.Mntent{FSName: "rootfs", Dir: "/etc/hostname", Type: "tmpfs", Opts: "host_passthrough"}, + ) for _, name := range libPaths { mnt = append(mnt, &check.Mntent{FSName: "\x00", Dir: name, Type: "\x00", Opts: "\x00", Freq: -1, Passno: -1}) } @@ -175,6 +178,12 @@ func TestHelperCheckContainer(t *testing.T) { } else if name != os.Args[5] { t.Errorf("Hostname: %q, want %q", name, os.Args[5]) } + + if p, err := os.ReadFile("/etc/hostname"); err != nil { + t.Fatalf("%v", err) + } else if string(p) != os.Args[5] { + t.Errorf("/etc/hostname: %q, want %q", string(p), os.Args[5]) + } }) t.Run("seccomp", func(t *testing.T) { check.MustAssertSeccomp() }) t.Run("mntent", func(t *testing.T) { check.MustAssertMounts("", "/proc/mounts", "/proc/self/fd/0") }) diff --git a/sandbox/sequential.go b/sandbox/sequential.go index f9c2e29..153de0a 100644 --- a/sandbox/sequential.go +++ b/sandbox/sequential.go @@ -6,6 +6,7 @@ import ( "math" "os" "path" + "slices" "syscall" "unsafe" ) @@ -262,3 +263,55 @@ func (f *Ops) Mkdir(dest string) *Ops { *f = append(*f, Mkdir(dest)) return f } + +func init() { gob.Register(new(Tmpfile)) } + +// Tmpfile places a file in container Path containing Data. +type Tmpfile struct { + Path string + Data []byte +} + +func (t *Tmpfile) apply(*Params) error { + if !path.IsAbs(t.Path) { + return msg.WrapErr(syscall.EBADE, + fmt.Sprintf("path %q is not absolute", t.Path)) + } + + var tmpPath string + if f, err := os.CreateTemp("/", "tmp.*"); err != nil { + return msg.WrapErr(err, err.Error()) + } else if _, err = f.Write(t.Data); err != nil { + return wrapErrSuffix(err, + "cannot write to intermediate file:") + } else if err = f.Close(); err != nil { + return wrapErrSuffix(err, + "cannot close intermediate file:") + } else { + tmpPath = f.Name() + } + + if err := bindMount(tmpPath, t.Path, BindSource|bindAbsolute); err != nil { + return err + } else if err = os.Remove(tmpPath); err != nil { + return msg.WrapErr(err, err.Error()) + } + return nil +} + +func (t *Tmpfile) Is(op Op) bool { + vt, ok := op.(*Tmpfile) + return ok && t.Path == vt.Path && slices.Equal(t.Data, vt.Data) +} +func (*Tmpfile) prefix() string { return "placing" } +func (t *Tmpfile) String() string { + return fmt.Sprintf("tmpfile %q (%d bytes)", t.Path, len(t.Data)) +} +func (f *Ops) Place(name string, data []byte) *Ops { *f = append(*f, &Tmpfile{name, data}); return f } +func (f *Ops) PlaceP(name string, dataP **[]byte) *Ops { + t := &Tmpfile{Path: name} + *dataP = &t.Data + + *f = append(*f, t) + return f +}