master
go 103 lines 2 KB
Raw
1 // FUSE mount/unmount lifecycle. go-fuse only builds on linux, darwin, and freebsd.
2 //go:build (linux || darwin || freebsd) && !nofuse
3
4 package mount
5
6 import (
7 "errors"
8 "fmt"
9 "sync"
10
11 "github.com/hanwen/go-fuse/v2/fs"
12 "github.com/hanwen/go-fuse/v2/fuse"
13 )
14
15 var ErrNotMounted = errors.New("not mounted")
16
17 // mount implements go-ipfs/fuse/mount.
18 type mount struct {
19 mpoint string
20 server *fuse.Server
21
22 active bool
23 activeLock *sync.RWMutex
24
25 unmountOnce sync.Once
26 }
27
28 // NewMount mounts a FUSE filesystem at a given location, and returns a Mount instance.
29 func NewMount(root fs.InodeEmbedder, mountpoint string, opts *fs.Options) (Mount, error) {
30 PlatformMountOpts(&opts.MountOptions)
31 server, err := fs.Mount(mountpoint, root, opts)
32 if err != nil {
33 return nil, fmt.Errorf("mounting %s: %w", mountpoint, err)
34 }
35
36 m := &mount{
37 mpoint: mountpoint,
38 server: server,
39 active: true,
40 activeLock: &sync.RWMutex{},
41 }
42
43 // Detect external unmount (e.g. fusermount -u) so IsActive
44 // returns false and Unmount returns ErrNotMounted.
45 go func() {
46 server.Wait()
47 m.setActive(false)
48 }()
49
50 log.Infof("Mounted %s", mountpoint)
51 return m, nil
52 }
53
54 // unmount is called exactly once to unmount this service.
55 func (m *mount) unmount() error {
56 log.Infof("Unmounting %s", m.MountPoint())
57
58 err := m.server.Unmount()
59 if err == nil {
60 m.setActive(false)
61 return nil
62 }
63 log.Warnf("fuse unmount err: %s", err)
64
65 // try mount.ForceUnmountManyTimes
66 if err := ForceUnmountManyTimes(m, 10); err != nil {
67 return err
68 }
69
70 log.Infof("Seemingly unmounted %s", m.MountPoint())
71 m.setActive(false)
72 return nil
73 }
74
75 func (m *mount) MountPoint() string {
76 return m.mpoint
77 }
78
79 func (m *mount) Unmount() error {
80 if !m.IsActive() {
81 return ErrNotMounted
82 }
83
84 var err error
85 m.unmountOnce.Do(func() {
86 err = m.unmount()
87 })
88
89 return err
90 }
91
92 func (m *mount) IsActive() bool {
93 m.activeLock.RLock()
94 defer m.activeLock.RUnlock()
95
96 return m.active
97 }
98
99 func (m *mount) setActive(a bool) {
100 m.activeLock.Lock()
101 m.active = a
102 m.activeLock.Unlock()
103 }