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 }