github.com/hanwen/go-fuse@v1.0.0/fuse/mount_linux.go (about)

     1  // Copyright 2016 the Go-FUSE 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  package fuse
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"strings"
    14  	"syscall"
    15  	"unsafe"
    16  )
    17  
    18  func unixgramSocketpair() (l, r *os.File, err error) {
    19  	fd, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
    20  	if err != nil {
    21  		return nil, nil, os.NewSyscallError("socketpair",
    22  			err.(syscall.Errno))
    23  	}
    24  	l = os.NewFile(uintptr(fd[0]), "socketpair-half1")
    25  	r = os.NewFile(uintptr(fd[1]), "socketpair-half2")
    26  	return
    27  }
    28  
    29  // Create a FUSE FS on the specified mount point.  The returned
    30  // mount point is always absolute.
    31  func mount(mountPoint string, opts *MountOptions, ready chan<- error) (fd int, err error) {
    32  	local, remote, err := unixgramSocketpair()
    33  	if err != nil {
    34  		return
    35  	}
    36  
    37  	defer local.Close()
    38  	defer remote.Close()
    39  
    40  	bin, err := fusermountBinary()
    41  	if err != nil {
    42  		return 0, err
    43  	}
    44  
    45  	cmd := []string{bin, mountPoint}
    46  	if s := opts.optionsStrings(); len(s) > 0 {
    47  		cmd = append(cmd, "-o", strings.Join(s, ","))
    48  	}
    49  	proc, err := os.StartProcess(bin,
    50  		cmd,
    51  		&os.ProcAttr{
    52  			Env:   []string{"_FUSE_COMMFD=3"},
    53  			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr, remote}})
    54  
    55  	if err != nil {
    56  		return
    57  	}
    58  
    59  	w, err := proc.Wait()
    60  	if err != nil {
    61  		return
    62  	}
    63  	if !w.Success() {
    64  		err = fmt.Errorf("fusermount exited with code %v\n", w.Sys())
    65  		return
    66  	}
    67  
    68  	fd, err = getConnection(local)
    69  	if err != nil {
    70  		return -1, err
    71  	}
    72  
    73  	// golang sets CLOEXEC on file descriptors when they are
    74  	// acquired through normal operations (e.g. open).
    75  	// Buf for fd, we have to set CLOEXEC manually
    76  	syscall.CloseOnExec(fd)
    77  
    78  	close(ready)
    79  	return fd, err
    80  }
    81  
    82  func unmount(mountPoint string) (err error) {
    83  	bin, err := fusermountBinary()
    84  	if err != nil {
    85  		return err
    86  	}
    87  	errBuf := bytes.Buffer{}
    88  	cmd := exec.Command(bin, "-u", mountPoint)
    89  	cmd.Stderr = &errBuf
    90  	err = cmd.Run()
    91  	if errBuf.Len() > 0 {
    92  		return fmt.Errorf("%s (code %v)\n",
    93  			errBuf.String(), err)
    94  	}
    95  	return err
    96  }
    97  
    98  func getConnection(local *os.File) (int, error) {
    99  	var data [4]byte
   100  	control := make([]byte, 4*256)
   101  
   102  	// n, oobn, recvflags, from, errno  - todo: error checking.
   103  	_, oobn, _, _,
   104  		err := syscall.Recvmsg(
   105  		int(local.Fd()), data[:], control[:], 0)
   106  	if err != nil {
   107  		return 0, err
   108  	}
   109  
   110  	message := *(*syscall.Cmsghdr)(unsafe.Pointer(&control[0]))
   111  	fd := *(*int32)(unsafe.Pointer(uintptr(unsafe.Pointer(&control[0])) + syscall.SizeofCmsghdr))
   112  
   113  	if message.Type != 1 {
   114  		return 0, fmt.Errorf("getConnection: recvmsg returned wrong control type: %d", message.Type)
   115  	}
   116  	if oobn <= syscall.SizeofCmsghdr {
   117  		return 0, fmt.Errorf("getConnection: too short control message. Length: %d", oobn)
   118  	}
   119  	if fd < 0 {
   120  		return 0, fmt.Errorf("getConnection: fd < 0: %d", fd)
   121  	}
   122  	return int(fd), nil
   123  }
   124  
   125  // lookPathFallback - search binary in PATH and, if that fails,
   126  // in fallbackDir. This is useful if PATH is possible empty.
   127  func lookPathFallback(file string, fallbackDir string) (string, error) {
   128  	binPath, err := exec.LookPath(file)
   129  	if err == nil {
   130  		return binPath, nil
   131  	}
   132  
   133  	abs := path.Join(fallbackDir, file)
   134  	return exec.LookPath(abs)
   135  }
   136  
   137  func fusermountBinary() (string, error) {
   138  	return lookPathFallback("fusermount", "/bin")
   139  }
   140  
   141  func umountBinary() (string, error) {
   142  	return lookPathFallback("umount", "/bin")
   143  }