2021-07-24 15:27:48 +02:00
|
|
|
//go:build linux
|
|
|
|
// +build linux
|
|
|
|
|
|
|
|
package mountlib
|
|
|
|
|
|
|
|
import (
|
2021-11-04 11:12:57 +01:00
|
|
|
"fmt"
|
2021-07-24 15:27:48 +02:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2023-07-22 13:59:20 +02:00
|
|
|
"github.com/moby/sys/mountinfo"
|
2021-07-24 15:27:48 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
pollInterval = 100 * time.Millisecond
|
|
|
|
)
|
|
|
|
|
|
|
|
// CheckMountEmpty checks if folder is not already a mountpoint.
|
2023-07-22 13:59:20 +02:00
|
|
|
// On Linux we use the OS-specific /proc/self/mountinfo API so the check won't access the path.
|
2021-07-24 15:27:48 +02:00
|
|
|
// Directories marked as "mounted" by autofs are considered not mounted.
|
|
|
|
func CheckMountEmpty(mountpoint string) error {
|
2022-06-08 22:54:39 +02:00
|
|
|
const msg = "directory already mounted, use --allow-non-empty to mount anyway: %s"
|
2021-07-24 15:27:48 +02:00
|
|
|
|
|
|
|
mountpointAbs, err := filepath.Abs(mountpoint)
|
|
|
|
if err != nil {
|
2021-11-04 11:12:57 +01:00
|
|
|
return fmt.Errorf("cannot get absolute path: %s: %w", mountpoint, err)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
|
|
|
|
2023-07-22 13:59:20 +02:00
|
|
|
infos, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(mountpointAbs))
|
2021-07-24 15:27:48 +02:00
|
|
|
if err != nil {
|
2023-07-22 13:59:20 +02:00
|
|
|
return fmt.Errorf("cannot get mounts: %w", err)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
2023-07-22 13:59:20 +02:00
|
|
|
|
2023-01-19 16:54:10 +01:00
|
|
|
foundAutofs := false
|
2023-07-22 13:59:20 +02:00
|
|
|
for _, info := range infos {
|
|
|
|
if info.FSType != "autofs" {
|
|
|
|
return fmt.Errorf(msg, mountpointAbs)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
2023-07-22 13:59:20 +02:00
|
|
|
foundAutofs = true
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
2023-01-19 16:54:10 +01:00
|
|
|
// It isn't safe to list an autofs in the middle of mounting
|
|
|
|
if foundAutofs {
|
|
|
|
return nil
|
|
|
|
}
|
2023-07-22 13:59:20 +02:00
|
|
|
|
2023-01-19 16:54:10 +01:00
|
|
|
return checkMountEmpty(mountpoint)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
|
|
|
|
2023-10-13 18:29:16 +02:00
|
|
|
// singleEntryFilter looks for a specific entry.
|
|
|
|
//
|
|
|
|
// It may appear more than once and we return all of them if so.
|
|
|
|
func singleEntryFilter(mp string) mountinfo.FilterFunc {
|
|
|
|
return func(m *mountinfo.Info) (skip, stop bool) {
|
|
|
|
return m.Mountpoint != mp, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-24 15:27:48 +02:00
|
|
|
// CheckMountReady checks whether mountpoint is mounted by rclone.
|
|
|
|
// Only mounts with type "rclone" or "fuse.rclone" count.
|
|
|
|
func CheckMountReady(mountpoint string) error {
|
2023-07-22 13:59:20 +02:00
|
|
|
const msg = "mount not ready: %s"
|
|
|
|
|
2021-07-24 15:27:48 +02:00
|
|
|
mountpointAbs, err := filepath.Abs(mountpoint)
|
|
|
|
if err != nil {
|
2021-11-04 11:12:57 +01:00
|
|
|
return fmt.Errorf("cannot get absolute path: %s: %w", mountpoint, err)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
2023-07-22 13:59:20 +02:00
|
|
|
|
2023-10-13 18:29:16 +02:00
|
|
|
infos, err := mountinfo.GetMounts(singleEntryFilter(mountpointAbs))
|
2021-07-24 15:27:48 +02:00
|
|
|
if err != nil {
|
2023-07-22 13:59:20 +02:00
|
|
|
return fmt.Errorf("cannot get mounts: %w", err)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
2023-07-22 13:59:20 +02:00
|
|
|
|
|
|
|
for _, info := range infos {
|
|
|
|
if strings.Contains(info.FSType, "rclone") {
|
2021-07-24 15:27:48 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2023-07-22 13:59:20 +02:00
|
|
|
|
|
|
|
return fmt.Errorf(msg, mountpointAbs)
|
2021-07-24 15:27:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// WaitMountReady waits until mountpoint is mounted by rclone.
|
|
|
|
func WaitMountReady(mountpoint string, timeout time.Duration) (err error) {
|
|
|
|
endTime := time.Now().Add(timeout)
|
|
|
|
for {
|
|
|
|
err = CheckMountReady(mountpoint)
|
|
|
|
delay := time.Until(endTime)
|
|
|
|
if err == nil || delay <= 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if delay > pollInterval {
|
|
|
|
delay = pollInterval
|
|
|
|
}
|
|
|
|
time.Sleep(delay)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|