mirror of
https://github.com/rclone/rclone.git
synced 2024-12-01 12:55:19 +01:00
29baa5888f
With automount the target mount drive appears twice in /proc/self/mountinfo. 379 27 0:70 / /mnt/rclone rw,relatime shared:433 - autofs systemd-1 rw,fd=57,... 566 379 0:90 / /mnt/rclone rw,nosuid,nodev,relatime shared:488 - fuse.rclone remote: rw,... Before this fix we only looked for the mount once in /proc/self/mountinfo. It finds the automount line and since this doesn't have fs type rclone it concludes the mount isn't ready yet. This patch makes rclone look through all the mounts and if any of them have fs type rclone it concludes the mount is ready. See: https://forum.rclone.org/t/systemd-mount-works-but-automount-does-not/42287/
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package mountlib
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/moby/sys/mountinfo"
|
|
)
|
|
|
|
const (
|
|
pollInterval = 100 * time.Millisecond
|
|
)
|
|
|
|
// CheckMountEmpty checks if folder is not already a mountpoint.
|
|
// On Linux we use the OS-specific /proc/self/mountinfo API so the check won't access the path.
|
|
// Directories marked as "mounted" by autofs are considered not mounted.
|
|
func CheckMountEmpty(mountpoint string) error {
|
|
const msg = "directory already mounted, use --allow-non-empty to mount anyway: %s"
|
|
|
|
mountpointAbs, err := filepath.Abs(mountpoint)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get absolute path: %s: %w", mountpoint, err)
|
|
}
|
|
|
|
infos, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(mountpointAbs))
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get mounts: %w", err)
|
|
}
|
|
|
|
foundAutofs := false
|
|
for _, info := range infos {
|
|
if info.FSType != "autofs" {
|
|
return fmt.Errorf(msg, mountpointAbs)
|
|
}
|
|
foundAutofs = true
|
|
}
|
|
// It isn't safe to list an autofs in the middle of mounting
|
|
if foundAutofs {
|
|
return nil
|
|
}
|
|
|
|
return checkMountEmpty(mountpoint)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
}
|
|
|
|
// CheckMountReady checks whether mountpoint is mounted by rclone.
|
|
// Only mounts with type "rclone" or "fuse.rclone" count.
|
|
func CheckMountReady(mountpoint string) error {
|
|
const msg = "mount not ready: %s"
|
|
|
|
mountpointAbs, err := filepath.Abs(mountpoint)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get absolute path: %s: %w", mountpoint, err)
|
|
}
|
|
|
|
infos, err := mountinfo.GetMounts(singleEntryFilter(mountpointAbs))
|
|
if err != nil {
|
|
return fmt.Errorf("cannot get mounts: %w", err)
|
|
}
|
|
|
|
for _, info := range infos {
|
|
if strings.Contains(info.FSType, "rclone") {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf(msg, mountpointAbs)
|
|
}
|
|
|
|
// 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
|
|
}
|