github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/fuse/mount/fuse.go (about)

     1  // +build !nofuse
     2  
     3  package mount
     4  
     5  import (
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse"
    10  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/bazil.org/fuse/fs"
    11  	"github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
    12  )
    13  
    14  // mount implements go-ipfs/fuse/mount
    15  type mount struct {
    16  	mpoint   string
    17  	filesys  fs.FS
    18  	fuseConn *fuse.Conn
    19  	// closeErr error
    20  
    21  	proc goprocess.Process
    22  }
    23  
    24  // Mount mounts a fuse fs.FS at a given location, and returns a Mount instance.
    25  // parent is a ContextGroup to bind the mount's ContextGroup to.
    26  func NewMount(p goprocess.Process, fsys fs.FS, mountpoint string, allow_other bool) (Mount, error) {
    27  	var conn *fuse.Conn
    28  	var err error
    29  
    30  	if allow_other {
    31  		conn, err = fuse.Mount(mountpoint, fuse.AllowOther())
    32  	} else {
    33  		conn, err = fuse.Mount(mountpoint)
    34  	}
    35  
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  
    40  	m := &mount{
    41  		mpoint:   mountpoint,
    42  		fuseConn: conn,
    43  		filesys:  fsys,
    44  		proc:     goprocess.WithParent(p), // link it to parent.
    45  	}
    46  	m.proc.SetTeardown(m.unmount)
    47  
    48  	// launch the mounting process.
    49  	if err := m.mount(); err != nil {
    50  		m.Unmount() // just in case.
    51  		return nil, err
    52  	}
    53  
    54  	return m, nil
    55  }
    56  
    57  func (m *mount) mount() error {
    58  	log.Infof("Mounting %s", m.MountPoint())
    59  
    60  	errs := make(chan error, 1)
    61  	go func() {
    62  		err := fs.Serve(m.fuseConn, m.filesys)
    63  		log.Debugf("Mounting %s -- fs.Serve returned (%s)", err)
    64  		if err != nil {
    65  			errs <- err
    66  		}
    67  	}()
    68  
    69  	// wait for the mount process to be done, or timed out.
    70  	select {
    71  	case <-time.After(MountTimeout):
    72  		return fmt.Errorf("Mounting %s timed out.", m.MountPoint())
    73  	case err := <-errs:
    74  		return err
    75  	case <-m.fuseConn.Ready:
    76  	}
    77  
    78  	// check if the mount process has an error to report
    79  	if err := m.fuseConn.MountError; err != nil {
    80  		return err
    81  	}
    82  
    83  	log.Infof("Mounted %s", m.MountPoint())
    84  	return nil
    85  }
    86  
    87  // umount is called exactly once to unmount this service.
    88  // note that closing the connection will not always unmount
    89  // properly. If that happens, we bring out the big guns
    90  // (mount.ForceUnmountManyTimes, exec unmount).
    91  func (m *mount) unmount() error {
    92  	log.Infof("Unmounting %s", m.MountPoint())
    93  
    94  	// try unmounting with fuse lib
    95  	err := fuse.Unmount(m.MountPoint())
    96  	if err == nil {
    97  		return nil
    98  	}
    99  	log.Warningf("fuse unmount err: %s", err)
   100  
   101  	// try closing the fuseConn
   102  	err = m.fuseConn.Close()
   103  	if err == nil {
   104  		return nil
   105  	}
   106  	if err != nil {
   107  		log.Warningf("fuse conn error: %s", err)
   108  	}
   109  
   110  	// try mount.ForceUnmountManyTimes
   111  	if err := ForceUnmountManyTimes(m, 10); err != nil {
   112  		return err
   113  	}
   114  
   115  	log.Infof("Seemingly unmounted %s", m.MountPoint())
   116  	return nil
   117  }
   118  
   119  func (m *mount) Process() goprocess.Process {
   120  	return m.proc
   121  }
   122  
   123  func (m *mount) MountPoint() string {
   124  	return m.mpoint
   125  }
   126  
   127  func (m *mount) Unmount() error {
   128  	// call Process Close(), which calls unmount() exactly once.
   129  	return m.proc.Close()
   130  }