From 2e3e5b606789abb5ca7c6ea8a07554a66e69841a Mon Sep 17 00:00:00 2001 From: Lucas Manning Date: Tue, 10 Jan 2023 12:07:24 -0800 Subject: [PATCH] Create a new test dimension that mounts a FUSE fs on /tmp. If you add "use_fusefs=True" to a syscall test, it generates a new test target denoted with _fuse at the end. This target runs a shim binary that mounts a FUSE fs at /tmp and runs a server that forwards all filesystem ops to /fuse. Once the FUSE is mounted, the shim execs the normal test binary. This new dimension should help test the correctness of our FUSE implementation. All relevant tests that already pass have the use_fusefs enabled. The rest are still broken. PiperOrigin-RevId: 501059036 --- WORKSPACE | 7 +++++ go.mod | 1 + go.sum | 3 ++ test/runner/BUILD | 1 + test/runner/defs.bzl | 18 ++++++++++++ test/runner/fuse/BUILD | 14 ++++++++++ test/runner/fuse/fuse.go | 59 ++++++++++++++++++++++++++++++++++++++++ test/runner/main.go | 21 ++++++++++++-- test/syscalls/BUILD | 3 ++ 9 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 test/runner/fuse/BUILD create mode 100644 test/runner/fuse/fuse.go diff --git a/WORKSPACE b/WORKSPACE index ee963ebc6..414ecdf5c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -1888,3 +1888,10 @@ go_repository( sum = "h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=", version = "v1.2.0", ) + +go_repository( + name = "com_github_hanwen_go_fuse_v2", + importpath = "github.com/hanwen/go-fuse/v2", + sum = "h1:fOTuYWxywhaliwMobGTP/6NUCgGdal6pCpuch4MHCnU=", + version = "v2.1.1-0.20220627082937-d01fda7edf17", +) diff --git a/go.mod b/go.mod index a59cc0773..26603ec95 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/gofuzz v1.0.0 // indirect github.com/googleapis/gnostic v0.4.0 // indirect + github.com/hanwen/go-fuse/v2 v2.1.1-0.20220627082937-d01fda7edf17 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.0 // indirect github.com/json-iterator/go v1.1.7 // indirect diff --git a/go.sum b/go.sum index e6c7248b7..8c6b69656 100644 --- a/go.sum +++ b/go.sum @@ -233,6 +233,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hanwen/go-fuse/v2 v2.1.1-0.20220627082937-d01fda7edf17 h1:fOTuYWxywhaliwMobGTP/6NUCgGdal6pCpuch4MHCnU= +github.com/hanwen/go-fuse/v2 v2.1.1-0.20220627082937-d01fda7edf17/go.mod h1:B1nGE/6RBFyBRC1RRnf23UpwCdyJ31eukw34oAKukAc= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= @@ -266,6 +268,7 @@ github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1 h1:zc0R6cOw98cMengLA0fvU5 github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a h1:+J2gw7Bw77w/fbK7wnNJJDKmw1IbWft2Ul5BzrG1Qm8= diff --git a/test/runner/BUILD b/test/runner/BUILD index 084cdbf5a..48a8ef3a1 100644 --- a/test/runner/BUILD +++ b/test/runner/BUILD @@ -8,6 +8,7 @@ go_binary( srcs = ["main.go"], data = [ "//runsc", + "//test/runner/fuse", "//test/runner/setup_container", ], visibility = ["//:sandbox"], diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl index 1277a441b..e3d1e6210 100644 --- a/test/runner/defs.bzl +++ b/test/runner/defs.bzl @@ -72,6 +72,7 @@ def _syscall_test( iouring = False, container = None, one_sandbox = True, + use_fusefs = False, **kwargs): # Prepend "runsc" to non-native platform names. full_platform = platform if platform == "native" else "runsc_" + platform @@ -84,6 +85,8 @@ def _syscall_test( name += "_overlay" if network != "none": name += "_" + network + "net" + if use_fusefs: + name += "_fuse" # Apply all tags. if tags == None: @@ -131,6 +134,7 @@ def _syscall_test( "--platform-support=" + platform_support, "--network=" + network, "--use-tmpfs=" + str(use_tmpfs), + "--use-fusefs=" + str(use_fusefs), "--file-access=" + file_access, "--overlay=" + str(overlay), "--add-host-communication=" + str(add_host_communication), @@ -163,6 +167,7 @@ def all_platforms(): def syscall_test( test, use_tmpfs = False, + use_fusefs = False, add_overlay = False, add_host_communication = False, add_hostinet = False, @@ -263,3 +268,16 @@ def syscall_test( file_access = "shared", **kwargs ) + if use_fusefs: + _syscall_test( + test = test, + platform = default_platform, + use_tmpfs = True, + use_fusefs = True, + add_host_communication = add_host_communication, + tags = platforms.get(default_platform, []) + tags, + debug = debug, + container = container, + one_sandbox = one_sandbox, + **kwargs + ) diff --git a/test/runner/fuse/BUILD b/test/runner/fuse/BUILD new file mode 100644 index 000000000..5211d33d4 --- /dev/null +++ b/test/runner/fuse/BUILD @@ -0,0 +1,14 @@ +load("//tools:defs.bzl", "go_binary") + +package(licenses = ["notice"]) + +go_binary( + name = "fuse", + srcs = ["fuse.go"], + visibility = ["//test/runner:__subpackages__"], + deps = [ + "//pkg/log", + "@com_github_hanwen_go_fuse_v2//fs:go_default_library", + "@com_github_hanwen_go_fuse_v2//fuse:go_default_library", + ], +) diff --git a/test/runner/fuse/fuse.go b/test/runner/fuse/fuse.go new file mode 100644 index 000000000..fd1bf99ec --- /dev/null +++ b/test/runner/fuse/fuse.go @@ -0,0 +1,59 @@ +// Copyright 2022 The gVisor Authors. +// +// 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. + +// Binary main starts a fuse server that forwards filesystem operations from +// /tmp to /fuse. +package main + +import ( + "os" + "os/exec" + + "github.com/hanwen/go-fuse/v2/fs" + "github.com/hanwen/go-fuse/v2/fuse" + "gvisor.dev/gvisor/pkg/log" +) + +func main() { + loopbackRoot, err := fs.NewLoopbackRoot("/fuse") + if err != nil { + log.Warningf("could not create loopback root: %v", err) + os.Exit(1) + } + opts := &fuse.MountOptions{DirectMount: true} + rawFS := fs.NewNodeFS(loopbackRoot, &fs.Options{}) + server, err := fuse.NewServer(rawFS, "/tmp", opts) + if err != nil { + log.Warningf("could not create fuse server: %v", err) + os.Exit(1) + } + + go server.Serve() + defer func() { + server.Unmount() + server.Wait() + }() + if err := server.WaitMount(); err != nil { + // We don't shutdown the serve loop. If the mount does + // not succeed, the loop won't work and exit. + log.Warningf(`Could not mount fuse submount "/tmp": %v`, err) + os.Exit(1) + } + cmd := exec.Command(os.Args[1], os.Args[2:]...) + if err := cmd.Run(); err != nil { + log.Warningf(err.Error()) + os.Exit(1) + } + os.Exit(0) +} diff --git a/test/runner/main.go b/test/runner/main.go index caedde091..626d84137 100644 --- a/test/runner/main.go +++ b/test/runner/main.go @@ -49,6 +49,7 @@ var ( platformSupport = flag.String("platform-support", "", "String passed to the test as GVISOR_PLATFORM_SUPPORT environment variable. Used to determine which syscall tests are expected to work with the current platform.") network = flag.String("network", "none", "network stack to run on (sandbox, host, none)") useTmpfs = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp") + useFUSEfs = flag.Bool("use-fusefs", false, "mounts a fusefs for /tmp") fileAccess = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode") overlay = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay") container = flag.Bool("container", false, "run tests in their own namespaces (user ns, network ns, etc), pretending to be root. Implicitly enabled if network=host, or if using network namespaces") @@ -376,8 +377,16 @@ func runTestCaseRunsc(testBin string, tc *gtest.TestCase, args []string, t *test if args == nil { args = tc.Args() } - spec := testutil.NewSpecWithArgs(append([]string{testBin}, args...)...) - + var spec *specs.Spec + if *useFUSEfs { + fuseServer, err := testutil.FindFile("test/runner/fuse/fuse") + if err != nil { + fatalf("cannot find fuse: %v", err) + } + spec = testutil.NewSpecWithArgs(append([]string{fuseServer, testBin}, args...)...) + } else { + spec = testutil.NewSpecWithArgs(append([]string{testBin}, args...)...) + } // Mark the root as writeable, as some tests attempt to // write to the rootfs, and expect EACCES, not EROFS. spec.Root.Readonly = false @@ -424,6 +433,14 @@ func runTestCaseRunsc(testBin string, tc *gtest.TestCase, args []string, t *test testTmpDir = "/tmp" } } + if *useFUSEfs { + // In fuse tests, the fuse server forwards all filesystem ops from /tmp + // to /fuse. + spec.Mounts = append(spec.Mounts, specs.Mount{ + Destination: "/fuse", + Type: "tmpfs", + }) + } // Set environment variables that indicate we are running in gVisor with // the given platform, network, and filesystem stack. diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 1cfd7c363..379fcfabf 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -327,6 +327,7 @@ syscall_test( syscall_test( add_overlay = True, test = "//test/syscalls/linux:mknod_test", + use_fusefs = True, ) syscall_test( @@ -913,12 +914,14 @@ syscall_test( syscall_test( add_overlay = True, test = "//test/syscalls/linux:statfs_test", + use_fusefs = True, use_tmpfs = True, # Test specifically relies on TEST_TMPDIR to be tmpfs. ) syscall_test( add_overlay = True, test = "//test/syscalls/linux:stat_test", + use_fusefs = True, ) syscall_test(