gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/runner/fuse/fuse.go (about)

     1  // Copyright 2022 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Binary main starts a fuse server that forwards filesystem operations from
    16  // /tmp to /fuse.
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	golog "log"
    22  	"os"
    23  	"os/exec"
    24  	"strings"
    25  
    26  	"github.com/hanwen/go-fuse/v2/fs"
    27  	"github.com/hanwen/go-fuse/v2/fuse"
    28  	"golang.org/x/sys/unix"
    29  	"gvisor.dev/gvisor/pkg/log"
    30  	"gvisor.dev/gvisor/runsc/specutils"
    31  )
    32  
    33  var (
    34  	dir   = flag.String("dir", "/tmp", "The directory to mount the fuse filesystem on.")
    35  	cmd   = flag.String("cmd", "", "Command to execute after starting the fuse server. If empty, just wait after starting.")
    36  	debug = flag.Bool("debug", true, "Whether to log FUSE traffic. Set to false for benchmarks.")
    37  )
    38  
    39  func waitOnMount(s *fuse.Server) {
    40  	if _, _, err := specutils.RetryEintr(func() (uintptr, uintptr, error) {
    41  		if err := s.WaitMount(); err != nil {
    42  			return 0, 0, err
    43  		}
    44  		return 0, 0, nil
    45  	}); err != nil {
    46  		// We don't shutdown the serve loop. If the mount does
    47  		// not succeed, the loop won't work and exit.
    48  		log.Warningf(`Could not mount fuse submount "/tmp": %v`, err)
    49  		os.Exit(1)
    50  	}
    51  }
    52  
    53  func main() {
    54  	flag.Parse()
    55  	loopbackRoot, err := fs.NewLoopbackRoot("/fuse")
    56  	if err != nil {
    57  		log.Warningf("could not create loopback root: %v", err)
    58  		os.Exit(1)
    59  	}
    60  	opts := &fuse.MountOptions{
    61  		DirectMountStrict: true,
    62  		Debug:             *debug,
    63  		AllowOther:        true,
    64  		// SingleThreaded adds locking the fuse server handler. We need to
    65  		// enable this so that the go race detector doesn't detect a data race, even
    66  		// if there isn't a logical race.
    67  		SingleThreaded: true,
    68  		Options:        []string{"default_permissions"},
    69  	}
    70  	rawFS := fs.NewNodeFS(loopbackRoot, &fs.Options{NullPermissions: true, Logger: golog.Default()})
    71  	server, err := fuse.NewServer(rawFS, *dir, opts)
    72  	if err != nil {
    73  		log.Warningf("could not create fuse server: %v", err)
    74  		os.Exit(1)
    75  	}
    76  	// Clear umask so that it doesn't affect the mode bits twice.
    77  	unix.Umask(0)
    78  
    79  	if *cmd == "" {
    80  		server.Serve()
    81  		waitOnMount(server)
    82  		return
    83  	}
    84  	go server.Serve()
    85  	waitOnMount(server)
    86  	defer func() {
    87  		server.Unmount()
    88  		server.Wait()
    89  	}()
    90  
    91  	cmdArgs := strings.Split(strings.Trim(*cmd, "\""), " ")
    92  	c := exec.Command(cmdArgs[0], cmdArgs[1:]...)
    93  	c.Stdout = os.Stdout
    94  	c.Stderr = os.Stderr
    95  	if err := c.Run(); err != nil {
    96  		log.Warningf(err.Error())
    97  		os.Exit(1)
    98  	}
    99  	os.Exit(0)
   100  }