mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-21 10:27:46 +08:00
Some checks are pending
CodeQL / codeql (push) Waiting to run
Docker Check / lint (push) Waiting to run
Docker Check / build (push) Waiting to run
Gateway Conformance / gateway-conformance (push) Waiting to run
Gateway Conformance / gateway-conformance-libp2p-experiment (push) Waiting to run
Go Build / go-build (push) Waiting to run
Go Check / go-check (push) Waiting to run
Go Lint / go-lint (push) Waiting to run
Go Test / go-test (push) Waiting to run
Interop / interop-prep (push) Waiting to run
Interop / helia-interop (push) Blocked by required conditions
Interop / ipfs-webui (push) Blocked by required conditions
Sharness / sharness-test (push) Waiting to run
Spell Check / spellcheck (push) Waiting to run
* ci: optimize build workflows - use go version from go.mod instead of hardcoding - group platforms by OS for parallel builds - remove legacy try-build targets * fix: checkout before setup-go in all workflows setup-go needs go.mod to be present, so checkout must happen first * chore: remove deprecated // +build syntax go 1.17+ uses //go:build, the old syntax is no longer needed * simplify: remove nofuse tag from CI workflows - workflows now rely on platform build constraints - keep make nofuse target for manual builds - remove unused appveyor.yml * ci: remove legacy travis variable and fix gateway-conformance - remove TRAVIS env variable from 4 workflows - fix gateway-conformance checkout path to match working-directory - replace deprecated cache-go-action with built-in setup-go caching
163 lines
3.2 KiB
Go
163 lines
3.2 KiB
Go
//go:build !nofuse && !windows && !openbsd && !netbsd && !plan9
|
|
|
|
package mount
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"bazil.org/fuse"
|
|
"bazil.org/fuse/fs"
|
|
)
|
|
|
|
var ErrNotMounted = errors.New("not mounted")
|
|
|
|
// mount implements go-ipfs/fuse/mount.
|
|
type mount struct {
|
|
mpoint string
|
|
filesys fs.FS
|
|
fuseConn *fuse.Conn
|
|
|
|
active bool
|
|
activeLock *sync.RWMutex
|
|
|
|
unmountOnce sync.Once
|
|
}
|
|
|
|
// Mount mounts a fuse fs.FS at a given location, and returns a Mount instance.
|
|
// ctx is parent is a ContextGroup to bind the mount's ContextGroup to.
|
|
func NewMount(fsys fs.FS, mountpoint string, allowOther bool) (Mount, error) {
|
|
var conn *fuse.Conn
|
|
var err error
|
|
|
|
mountOpts := []fuse.MountOption{
|
|
fuse.MaxReadahead(64 * 1024 * 1024),
|
|
fuse.AsyncRead(),
|
|
}
|
|
|
|
if allowOther {
|
|
mountOpts = append(mountOpts, fuse.AllowOther())
|
|
}
|
|
conn, err = fuse.Mount(mountpoint, mountOpts...)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m := &mount{
|
|
mpoint: mountpoint,
|
|
fuseConn: conn,
|
|
filesys: fsys,
|
|
active: false,
|
|
activeLock: &sync.RWMutex{},
|
|
}
|
|
|
|
// launch the mounting process.
|
|
if err = m.mount(); err != nil {
|
|
_ = m.Unmount() // just in case.
|
|
return nil, err
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func (m *mount) mount() error {
|
|
log.Infof("Mounting %s", m.MountPoint())
|
|
|
|
errs := make(chan error, 1)
|
|
go func() {
|
|
// fs.Serve blocks until the filesystem is unmounted.
|
|
err := fs.Serve(m.fuseConn, m.filesys)
|
|
log.Debugf("%s is unmounted", m.MountPoint())
|
|
if err != nil {
|
|
log.Debugf("fs.Serve returned (%s)", err)
|
|
errs <- err
|
|
}
|
|
m.setActive(false)
|
|
}()
|
|
|
|
// wait for the mount process to be done, or timed out.
|
|
select {
|
|
case <-time.After(MountTimeout):
|
|
return fmt.Errorf("mounting %s timed out", m.MountPoint())
|
|
case err := <-errs:
|
|
return err
|
|
case <-m.fuseConn.Ready:
|
|
}
|
|
|
|
// check if the mount process has an error to report
|
|
if err := m.fuseConn.MountError; err != nil {
|
|
return err
|
|
}
|
|
|
|
m.setActive(true)
|
|
|
|
log.Infof("Mounted %s", m.MountPoint())
|
|
return nil
|
|
}
|
|
|
|
// unmount is called exactly once to unmount this service.
|
|
// note that closing the connection will not always unmount
|
|
// properly. If that happens, we bring out the big guns
|
|
// (mount.ForceUnmountManyTimes, exec unmount).
|
|
func (m *mount) unmount() error {
|
|
log.Infof("Unmounting %s", m.MountPoint())
|
|
|
|
// try unmounting with fuse lib
|
|
err := fuse.Unmount(m.MountPoint())
|
|
if err == nil {
|
|
m.setActive(false)
|
|
return nil
|
|
}
|
|
log.Warnf("fuse unmount err: %s", err)
|
|
|
|
// try closing the fuseConn
|
|
err = m.fuseConn.Close()
|
|
if err == nil {
|
|
m.setActive(false)
|
|
return nil
|
|
}
|
|
log.Warnf("fuse conn error: %s", err)
|
|
|
|
// try mount.ForceUnmountManyTimes
|
|
if err := ForceUnmountManyTimes(m, 10); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Infof("Seemingly unmounted %s", m.MountPoint())
|
|
m.setActive(false)
|
|
return nil
|
|
}
|
|
|
|
func (m *mount) MountPoint() string {
|
|
return m.mpoint
|
|
}
|
|
|
|
func (m *mount) Unmount() error {
|
|
if !m.IsActive() {
|
|
return ErrNotMounted
|
|
}
|
|
|
|
var err error
|
|
m.unmountOnce.Do(func() {
|
|
err = m.unmount()
|
|
})
|
|
|
|
return err
|
|
}
|
|
|
|
func (m *mount) IsActive() bool {
|
|
m.activeLock.RLock()
|
|
defer m.activeLock.RUnlock()
|
|
|
|
return m.active
|
|
}
|
|
|
|
func (m *mount) setActive(a bool) {
|
|
m.activeLock.Lock()
|
|
m.active = a
|
|
m.activeLock.Unlock()
|
|
}
|