github.com/rminnich/u-root@v7.0.0+incompatible/cmds/core/fusermount/fusermount.go (about)

     1  // Copyright 2018-2019 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build linux
     6  
     7  // fusermount is a very limited replacement for the C fusermount.  It
     8  // is invoked by other programs, or interactively only to unmount.
     9  //
    10  // Synopsis:
    11  //     fusermount [-u|--unmount] [-z|--lazy] [-v|--verbose] <mountpoint>
    12  //
    13  // For mounting, per the FUSE model, the environment variable
    14  // _FUSE_COMMFD must have the value of a file descriptor variable on
    15  // which we pass the fuse fd.
    16  //
    17  // There is some checking we don't do, e.g. for the number of active
    18  // mount points.  Last time I checked, that's the kind of stuff
    19  // kernels do.
    20  //
    21  // Description:
    22  //	invoke fuse mount operations
    23  package main
    24  
    25  import (
    26  	"fmt"
    27  	"log"
    28  	"os"
    29  	"path/filepath"
    30  	"strconv"
    31  	"syscall"
    32  
    33  	flag "github.com/spf13/pflag"
    34  
    35  	"golang.org/x/sys/unix"
    36  )
    37  
    38  const (
    39  	// CommFD is the environment variable which contains the comms fd.
    40  	CommFD  = "_FUSE_COMMFD"
    41  	fuseDev = "/dev/fuse"
    42  )
    43  
    44  var (
    45  	unmount = flag.BoolP("unmount", "u", false, "unmount")
    46  	lazy    = flag.BoolP("lazy", "z", false, "lazy unmount")
    47  	verbose = flag.BoolP("verbose", "v", false, "verbose")
    48  	debug   = func(string, ...interface{}) {}
    49  	mpt     string
    50  )
    51  
    52  var (
    53  	help = "usage: fusermount [-u|--unmount] [-z|--lazy] [-v|--verbose] <mountpoint>"
    54  )
    55  
    56  func usage() {
    57  	log.Fatalf(help)
    58  }
    59  
    60  func umount(n string) error {
    61  	// we're not doing all the folderol of standard
    62  	// fusermount for euid() == 0.
    63  	// Let's see how that works out.
    64  	flags := 0
    65  	if *lazy {
    66  		flags |= unix.MNT_DETACH
    67  	}
    68  
    69  	// TODO: anything we need here if unit.Getuid() == 0.
    70  	// So far there is nothing.
    71  	err := unix.Unmount(n, flags)
    72  	return err
    73  }
    74  
    75  func openFUSE() (int, error) {
    76  	return unix.Open("/dev/fuse", unix.O_RDWR, 0)
    77  }
    78  
    79  // MountPointOK performs validation on the mountpoint.
    80  // Bury all your magic in here.
    81  func MountPointOK(mpt string) error {
    82  	// We wait until we can drop privs to test the mpt
    83  	// parameter, since ability to walk the path can
    84  	// differ for root and the real user id.
    85  	if err := dropPrivs(); err != nil {
    86  		return err
    87  	}
    88  	defer restorePrivs()
    89  	mpt = filepath.Clean(mpt)
    90  	r, err := filepath.EvalSymlinks(mpt)
    91  	if err != nil {
    92  		return err
    93  	}
    94  	if r != mpt {
    95  		return fmt.Errorf("resolved path %q and mountpoint %q are not the same", r, mpt)
    96  	}
    97  	// I'm not sure why fusermount wants to open the mountpoint, so let's mot for now.
    98  	// And, for now, directories only? We don't see a current need to mount
    99  	// FUSE on any other type of file.
   100  	if err := os.Chdir(mpt); err != nil {
   101  		return err
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  func getCommFD() (int, error) {
   108  	commfd, ok := os.LookupEnv(CommFD)
   109  	if !ok {
   110  		return -1, fmt.Errorf(CommFD + "was not set and this program can't be used interactively")
   111  	}
   112  	debug("CommFD %v", commfd)
   113  
   114  	cfd, err := strconv.Atoi(commfd)
   115  	if err != nil {
   116  		return -1, fmt.Errorf("%s: %v", CommFD, err)
   117  	}
   118  	debug("CFD is %v", cfd)
   119  	var st unix.Stat_t
   120  	if err := unix.Fstat(cfd, &st); err != nil {
   121  		return -1, fmt.Errorf("_FUSE_COMMFD: %d: %v", cfd, err)
   122  	}
   123  	debug("cfd stat is %v", st)
   124  
   125  	return cfd, nil
   126  }
   127  
   128  func doMount(fd int) error {
   129  	var flags = uintptr(unix.MS_NODEV | unix.MS_NOSUID)
   130  	// From the kernel:
   131  	//if (!d->fd_present || !d->rootmode_present ||
   132  	//	!d->user_id_present || !d->group_id_present)
   133  	//		return 0;
   134  	// Yeah. You get EINVAL if any one of these is not set.
   135  	// Docs? what? Docs?
   136  	return unix.Mount("nodev", ".", "fuse", flags, fmt.Sprintf("rootmode=%o,user_id=0,group_id=0,fd=%d", unix.S_IFDIR, fd))
   137  }
   138  
   139  // returnResult returns the result from earlier operations.
   140  // It is called with the control fd, a FUSE fd, and an error.
   141  // If the error is not nil, then we are shutting down the cfd;
   142  // If it is nil then we try to send the fd back.
   143  // We return either e or the error result and e
   144  func returnResult(cfd, ffd int, e error) error {
   145  	if e != nil {
   146  		if err := unix.Shutdown(cfd, unix.SHUT_RDWR); err != nil {
   147  			return fmt.Errorf("shutting down after failed mount with %v: %v", e, err)
   148  		}
   149  		return e
   150  	}
   151  	oob := unix.UnixRights(int(ffd))
   152  	if err := unix.Sendmsg(cfd, []byte(""), oob, nil, 0); err != nil {
   153  		return fmt.Errorf("%s: %d: %v", CommFD, cfd, err)
   154  	}
   155  	return nil
   156  }
   157  
   158  func main() {
   159  	flag.Parse()
   160  
   161  	if *verbose {
   162  		debug = log.Printf
   163  	}
   164  
   165  	if len(flag.Args()) != 1 {
   166  		usage()
   167  	}
   168  	mpt = flag.Arg(0)
   169  	debug("mpt %v", mpt)
   170  
   171  	// We let "ability to open /dev/fuse" stand in as an indicator or
   172  	// "we support FUSE".
   173  	FuseFD, err := openFUSE()
   174  	if err != nil {
   175  		log.Printf("%v", err)
   176  		os.Exit(int(syscall.ENOENT))
   177  	}
   178  	debug("FuseFD %v", FuseFD)
   179  
   180  	// Bad design. All they had to do was make a -z and -u and have
   181  	// them both mean unmount. Oh well.
   182  	if *lazy && !*unmount {
   183  		log.Fatalf("-z can only be used with -u")
   184  	}
   185  
   186  	// Fuse has to be seen to be believed.
   187  	// The only interactive use of fusermount is to unmount
   188  	if *unmount {
   189  		if err := umount(mpt); err != nil {
   190  			log.Fatal(err)
   191  		}
   192  		return
   193  	}
   194  
   195  	if err := MountPointOK(mpt); err != nil {
   196  		log.Fatal(err)
   197  	}
   198  
   199  	if err := preMount(); err != nil {
   200  		log.Fatal(err)
   201  	}
   202  
   203  	cfd, err := getCommFD()
   204  	if err != nil {
   205  		log.Fatal(err)
   206  	}
   207  
   208  	if err := doMount(FuseFD); err != nil {
   209  		log.Fatal(err)
   210  	}
   211  
   212  	if err := returnResult(cfd, FuseFD, err); err != nil {
   213  		log.Fatal(err)
   214  	}
   215  }