github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+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 }