summaryrefslogtreecommitdiffhomepage
path: root/runsc/container/shared_volume_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/container/shared_volume_test.go')
-rw-r--r--runsc/container/shared_volume_test.go267
1 files changed, 267 insertions, 0 deletions
diff --git a/runsc/container/shared_volume_test.go b/runsc/container/shared_volume_test.go
new file mode 100644
index 000000000..8f81ed630
--- /dev/null
+++ b/runsc/container/shared_volume_test.go
@@ -0,0 +1,267 @@
+// Copyright 2019 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package container
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "gvisor.googlesource.com/gvisor/pkg/sentry/control"
+ "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
+ "gvisor.googlesource.com/gvisor/runsc/boot"
+ "gvisor.googlesource.com/gvisor/runsc/test/testutil"
+)
+
+// TestSharedVolume checks that modifications to a volume mount are propagated
+// into and out of the sandbox.
+func TestSharedVolume(t *testing.T) {
+ conf := testutil.TestConfig()
+ conf.FileAccess = boot.FileAccessShared
+ t.Logf("Running test with conf: %+v", conf)
+
+ // Main process just sleeps. We will use "exec" to probe the state of
+ // the filesystem.
+ spec := testutil.NewSpecWithArgs("sleep", "1000")
+
+ dir, err := ioutil.TempDir(testutil.TmpDir(), "shared-volume-test")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+
+ rootDir, bundleDir, err := testutil.SetupContainer(spec, conf)
+ if err != nil {
+ t.Fatalf("error setting up container: %v", err)
+ }
+ defer os.RemoveAll(rootDir)
+ defer os.RemoveAll(bundleDir)
+
+ // Create and start the container.
+ c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "")
+ if err != nil {
+ t.Fatalf("error creating container: %v", err)
+ }
+ defer c.Destroy()
+ if err := c.Start(conf); err != nil {
+ t.Fatalf("error starting container: %v", err)
+ }
+
+ // File that will be used to check consistency inside/outside sandbox.
+ filename := filepath.Join(dir, "file")
+
+ // File does not exist yet. Reading from the sandbox should fail.
+ argsTestFile := &control.ExecArgs{
+ Filename: "/usr/bin/test",
+ Argv: []string{"test", "-f", filename},
+ }
+ if ws, err := c.executeSync(argsTestFile); err != nil {
+ t.Fatalf("unexpected error testing file %q: %v", filename, err)
+ } else if ws.ExitStatus() == 0 {
+ t.Errorf("test %q exited with code %v, wanted not zero", ws.ExitStatus(), err)
+ }
+
+ // Create the file from outside of the sandbox.
+ if err := ioutil.WriteFile(filename, []byte("foobar"), 0777); err != nil {
+ t.Fatalf("error writing to file %q: %v", filename, err)
+ }
+
+ // Now we should be able to test the file from within the sandbox.
+ if ws, err := c.executeSync(argsTestFile); err != nil {
+ t.Fatalf("unexpected error testing file %q: %v", filename, err)
+ } else if ws.ExitStatus() != 0 {
+ t.Errorf("test %q exited with code %v, wanted zero", filename, ws.ExitStatus())
+ }
+
+ // Rename the file from outside of the sandbox.
+ newFilename := filepath.Join(dir, "newfile")
+ if err := os.Rename(filename, newFilename); err != nil {
+ t.Fatalf("os.Rename(%q, %q) failed: %v", filename, newFilename, err)
+ }
+
+ // File should no longer exist at the old path within the sandbox.
+ if ws, err := c.executeSync(argsTestFile); err != nil {
+ t.Fatalf("unexpected error testing file %q: %v", filename, err)
+ } else if ws.ExitStatus() == 0 {
+ t.Errorf("test %q exited with code %v, wanted not zero", filename, ws.ExitStatus())
+ }
+
+ // We should be able to test the new filename from within the sandbox.
+ argsTestNewFile := &control.ExecArgs{
+ Filename: "/usr/bin/test",
+ Argv: []string{"test", "-f", newFilename},
+ }
+ if ws, err := c.executeSync(argsTestNewFile); err != nil {
+ t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
+ } else if ws.ExitStatus() != 0 {
+ t.Errorf("test %q exited with code %v, wanted zero", newFilename, ws.ExitStatus())
+ }
+
+ // Delete the renamed file from outside of the sandbox.
+ if err := os.Remove(newFilename); err != nil {
+ t.Fatalf("error removing file %q: %v", filename, err)
+ }
+
+ // Renamed file should no longer exist at the old path within the sandbox.
+ if ws, err := c.executeSync(argsTestNewFile); err != nil {
+ t.Fatalf("unexpected error testing file %q: %v", newFilename, err)
+ } else if ws.ExitStatus() == 0 {
+ t.Errorf("test %q exited with code %v, wanted not zero", newFilename, ws.ExitStatus())
+ }
+
+ // Now create the file from WITHIN the sandbox.
+ argsTouch := &control.ExecArgs{
+ Filename: "/usr/bin/touch",
+ Argv: []string{"touch", filename},
+ KUID: auth.KUID(os.Getuid()),
+ KGID: auth.KGID(os.Getgid()),
+ }
+ if ws, err := c.executeSync(argsTouch); err != nil {
+ t.Fatalf("unexpected error touching file %q: %v", filename, err)
+ } else if ws.ExitStatus() != 0 {
+ t.Errorf("touch %q exited with code %v, wanted zero", filename, ws.ExitStatus())
+ }
+
+ // File should exist outside the sandbox.
+ if _, err := os.Stat(filename); err != nil {
+ t.Errorf("stat %q got error %v, wanted nil", filename, err)
+ }
+
+ // File should exist outside the sandbox.
+ if _, err := os.Stat(filename); err != nil {
+ t.Errorf("stat %q got error %v, wanted nil", filename, err)
+ }
+
+ // Delete the file from within the sandbox.
+ argsRemove := &control.ExecArgs{
+ Filename: "/bin/rm",
+ Argv: []string{"rm", filename},
+ }
+ if ws, err := c.executeSync(argsRemove); err != nil {
+ t.Fatalf("unexpected error removing file %q: %v", filename, err)
+ } else if ws.ExitStatus() != 0 {
+ t.Errorf("remove %q exited with code %v, wanted zero", filename, ws.ExitStatus())
+ }
+
+ // File should not exist outside the sandbox.
+ if _, err := os.Stat(filename); !os.IsNotExist(err) {
+ t.Errorf("stat %q got error %v, wanted ErrNotExist", filename, err)
+ }
+}
+
+func checkFile(c *Container, filename string, want []byte) error {
+ cpy := filename + ".copy"
+ argsCp := &control.ExecArgs{
+ Filename: "/bin/cp",
+ Argv: []string{"cp", "-f", filename, cpy},
+ }
+ if _, err := c.executeSync(argsCp); err != nil {
+ return fmt.Errorf("unexpected error copying file %q to %q: %v", filename, cpy, err)
+ }
+ got, err := ioutil.ReadFile(cpy)
+ if err != nil {
+ return fmt.Errorf("Error reading file %q: %v", filename, err)
+ }
+ if !bytes.Equal(got, want) {
+ return fmt.Errorf("file content inside the sandbox is wrong, got: %q, want: %q", got, want)
+ }
+ return nil
+}
+
+// TestSharedVolumeFile tests that changes to file content outside the sandbox
+// is reflected inside.
+func TestSharedVolumeFile(t *testing.T) {
+ conf := testutil.TestConfig()
+ conf.FileAccess = boot.FileAccessShared
+ t.Logf("Running test with conf: %+v", conf)
+
+ // Main process just sleeps. We will use "exec" to probe the state of
+ // the filesystem.
+ spec := testutil.NewSpecWithArgs("sleep", "1000")
+
+ dir, err := ioutil.TempDir(testutil.TmpDir(), "shared-volume-test")
+ if err != nil {
+ t.Fatalf("TempDir failed: %v", err)
+ }
+
+ rootDir, bundleDir, err := testutil.SetupContainer(spec, conf)
+ if err != nil {
+ t.Fatalf("error setting up container: %v", err)
+ }
+ defer os.RemoveAll(rootDir)
+ defer os.RemoveAll(bundleDir)
+
+ // Create and start the container.
+ c, err := Create(testutil.UniqueContainerID(), spec, conf, bundleDir, "", "", "")
+ if err != nil {
+ t.Fatalf("error creating container: %v", err)
+ }
+ defer c.Destroy()
+ if err := c.Start(conf); err != nil {
+ t.Fatalf("error starting container: %v", err)
+ }
+
+ // File that will be used to check consistency inside/outside sandbox.
+ filename := filepath.Join(dir, "file")
+
+ // Write file from outside the container and check that the same content is
+ // read inside.
+ want := []byte("host-")
+ if err := ioutil.WriteFile(filename, []byte(want), 0666); err != nil {
+ t.Fatalf("Error writing to %q: %v", filename, err)
+ }
+ if err := checkFile(c, filename, want); err != nil {
+ t.Fatal(err.Error())
+ }
+
+ // Append to file inside the container and check that content is not lost.
+ argsAppend := &control.ExecArgs{
+ Filename: "/bin/bash",
+ Argv: []string{"bash", "-c", "echo -n sandbox- >> " + filename},
+ }
+ if _, err := c.executeSync(argsAppend); err != nil {
+ t.Fatalf("unexpected error appending file %q: %v", filename, err)
+ }
+ want = []byte("host-sandbox-")
+ if err := checkFile(c, filename, want); err != nil {
+ t.Fatal(err.Error())
+ }
+
+ // Write again from outside the container and check that the same content is
+ // read inside.
+ f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0)
+ if err != nil {
+ t.Fatalf("Error openning file %q: %v", filename, err)
+ }
+ defer f.Close()
+ if _, err := f.Write([]byte("host")); err != nil {
+ t.Fatalf("Error writing to file %q: %v", filename, err)
+ }
+ want = []byte("host-sandbox-host")
+ if err := checkFile(c, filename, want); err != nil {
+ t.Fatal(err.Error())
+ }
+
+ // Shrink file outside and check that the same content is read inside.
+ if err := f.Truncate(5); err != nil {
+ t.Fatalf("Error truncating file %q: %v", filename, err)
+ }
+ want = want[:5]
+ if err := checkFile(c, filename, want); err != nil {
+ t.Fatal(err.Error())
+ }
+}