kubo/fuse/mount/mount.go
Andrew Gillis 90b73d2ad2
refactor: remove goprocess (#10872)
* refactor: remove goprocess

The `goprocess` package is no longer needed. It can be replaces by modern `context` and `context.AfterFunc`.

* mod tidy

* log unmount errors on shutdown

* Do not log non-mounted errors on shutdown

* Use WaitGroup associated with IPFS node to wait for services to whutdown

* Prefer explicit Close to context.ArterFunc

* Do not use node-level WaitGroup

* Unmount for non-supported platforms

* fix return values

* test: daemon shuts down gracefully

make sure ongoing operations dont block shutdown

* test(cli): add TestFUSE

* test: smarter RequiresFUSE

opportunistically run FUSE tests if env has fusermount
and TEST_FUSE was not explicitly set

* docs: changelog

---------

Co-authored-by: gammazero <gammazero@users.noreply.github.com>
Co-authored-by: Marcin Rataj <lidel@lidel.org>
2025-08-06 00:33:45 +02:00

103 lines
2.1 KiB
Go

// package mount provides a simple abstraction around a mount point
package mount
import (
"fmt"
"io"
"os/exec"
"runtime"
"time"
logging "github.com/ipfs/go-log/v2"
)
var log = logging.Logger("mount")
var MountTimeout = time.Second * 5
// Mount represents a filesystem mount.
type Mount interface {
// MountPoint is the path at which this mount is mounted
MountPoint() string
// Unmounts the mount
Unmount() error
// Checks if the mount is still active.
IsActive() bool
}
// ForceUnmount attempts to forcibly unmount a given mount.
// It does so by calling diskutil or fusermount directly.
func ForceUnmount(m Mount) error {
point := m.MountPoint()
log.Warnf("Force-Unmounting %s...", point)
cmd, err := UnmountCmd(point)
if err != nil {
return err
}
errc := make(chan error, 1)
go func() {
defer close(errc)
// try vanilla unmount first.
if err := exec.Command("umount", point).Run(); err == nil {
return
}
// retry to unmount with the fallback cmd
errc <- cmd.Run()
}()
select {
case <-time.After(7 * time.Second):
return fmt.Errorf("umount timeout")
case err := <-errc:
return err
}
}
// UnmountCmd creates an exec.Cmd that is GOOS-specific
// for unmount a FUSE mount.
func UnmountCmd(point string) (*exec.Cmd, error) {
switch runtime.GOOS {
case "darwin":
return exec.Command("diskutil", "umount", "force", point), nil
case "linux":
return exec.Command("fusermount", "-u", point), nil
default:
return nil, fmt.Errorf("unmount: unimplemented")
}
}
// ForceUnmountManyTimes attempts to forcibly unmount a given mount,
// many times. It does so by calling diskutil or fusermount directly.
// Attempts a given number of times.
func ForceUnmountManyTimes(m Mount, attempts int) error {
var err error
for i := 0; i < attempts; i++ {
err = ForceUnmount(m)
if err == nil {
return err
}
<-time.After(time.Millisecond * 500)
}
return fmt.Errorf("unmount %s failed after 10 seconds of trying", m.MountPoint())
}
type closer struct {
M Mount
}
func (c *closer) Close() error {
log.Warn(" (c *closer) Close(),", c.M.MountPoint())
return c.M.Unmount()
}
func Closer(m Mount) io.Closer {
return &closer{m}
}