master
go 105 lines 2.25 KB
Raw
1 // package mount provides a simple abstraction around a mount point
2 package mount
3
4 import (
5 "fmt"
6 "io"
7 "os/exec"
8 "runtime"
9 "time"
10
11 logging "github.com/ipfs/go-log/v2"
12 )
13
14 var log = logging.Logger("mount")
15
16 var MountTimeout = time.Second * 5
17
18 // Mount represents a filesystem mount.
19 type Mount interface {
20 // MountPoint is the path at which this mount is mounted
21 MountPoint() string
22
23 // Unmounts the mount
24 Unmount() error
25
26 // Checks if the mount is still active.
27 IsActive() bool
28 }
29
30 // ForceUnmount attempts to forcibly unmount a given mount.
31 // It does so by calling diskutil or fusermount directly.
32 func ForceUnmount(m Mount) error {
33 point := m.MountPoint()
34 log.Warnf("Force-Unmounting %s...", point)
35
36 cmd, err := UnmountCmd(point)
37 if err != nil {
38 return err
39 }
40
41 errc := make(chan error, 1)
42 go func() {
43 defer close(errc)
44
45 // try vanilla unmount first.
46 if err := exec.Command("umount", point).Run(); err == nil {
47 return
48 }
49
50 // retry to unmount with the fallback cmd
51 errc <- cmd.Run()
52 }()
53
54 select {
55 case <-time.After(7 * time.Second):
56 return fmt.Errorf("umount timeout")
57 case err := <-errc:
58 return err
59 }
60 }
61
62 // UnmountCmd creates an exec.Cmd that is GOOS-specific
63 // for unmount a FUSE mount.
64 func UnmountCmd(point string) (*exec.Cmd, error) {
65 switch runtime.GOOS {
66 case "darwin":
67 return exec.Command("diskutil", "umount", "force", point), nil
68 case "linux":
69 if _, err := exec.LookPath("fusermount3"); err == nil {
70 return exec.Command("fusermount3", "-u", point), nil
71 }
72 return exec.Command("fusermount", "-u", point), nil
73 default:
74 return nil, fmt.Errorf("unmount: unimplemented")
75 }
76 }
77
78 // ForceUnmountManyTimes attempts to forcibly unmount a given mount,
79 // many times. It does so by calling diskutil or fusermount directly.
80 // Attempts a given number of times.
81 func ForceUnmountManyTimes(m Mount, attempts int) error {
82 var err error
83 for range attempts {
84 err = ForceUnmount(m)
85 if err == nil {
86 return err
87 }
88
89 <-time.After(time.Millisecond * 500)
90 }
91 return fmt.Errorf("unmount %s failed after 10 seconds of trying", m.MountPoint())
92 }
93
94 type closer struct {
95 M Mount
96 }
97
98 func (c *closer) Close() error {
99 log.Warn(" (c *closer) Close(),", c.M.MountPoint())
100 return c.M.Unmount()
101 }
102
103 func Closer(m Mount) io.Closer {
104 return &closer{m}
105 }