github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/cmd/mountlib/check_linux.go (about)

     1  //go:build linux
     2  
     3  package mountlib
     4  
     5  import (
     6  	"fmt"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/moby/sys/mountinfo"
    11  )
    12  
    13  // CheckMountEmpty checks if folder is not already a mountpoint.
    14  // On Linux we use the OS-specific /proc/self/mountinfo API so the check won't access the path.
    15  // Directories marked as "mounted" by autofs are considered not mounted.
    16  func CheckMountEmpty(mountpoint string) error {
    17  	const msg = "directory already mounted, use --allow-non-empty to mount anyway: %s"
    18  
    19  	mountpointAbs, err := filepath.Abs(mountpoint)
    20  	if err != nil {
    21  		return fmt.Errorf("cannot get absolute path: %s: %w", mountpoint, err)
    22  	}
    23  
    24  	infos, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(mountpointAbs))
    25  	if err != nil {
    26  		return fmt.Errorf("cannot get mounts: %w", err)
    27  	}
    28  
    29  	foundAutofs := false
    30  	for _, info := range infos {
    31  		if info.FSType != "autofs" {
    32  			return fmt.Errorf(msg, mountpointAbs)
    33  		}
    34  		foundAutofs = true
    35  	}
    36  	// It isn't safe to list an autofs in the middle of mounting
    37  	if foundAutofs {
    38  		return nil
    39  	}
    40  
    41  	return checkMountEmpty(mountpoint)
    42  }
    43  
    44  // singleEntryFilter looks for a specific entry.
    45  //
    46  // It may appear more than once and we return all of them if so.
    47  func singleEntryFilter(mp string) mountinfo.FilterFunc {
    48  	return func(m *mountinfo.Info) (skip, stop bool) {
    49  		return m.Mountpoint != mp, false
    50  	}
    51  }
    52  
    53  // CheckMountReady checks whether mountpoint is mounted by rclone.
    54  // Only mounts with type "rclone" or "fuse.rclone" count.
    55  func CheckMountReady(mountpoint string) error {
    56  	const msg = "mount not ready: %s"
    57  
    58  	mountpointAbs, err := filepath.Abs(mountpoint)
    59  	if err != nil {
    60  		return fmt.Errorf("cannot get absolute path: %s: %w", mountpoint, err)
    61  	}
    62  
    63  	infos, err := mountinfo.GetMounts(singleEntryFilter(mountpointAbs))
    64  	if err != nil {
    65  		return fmt.Errorf("cannot get mounts: %w", err)
    66  	}
    67  
    68  	for _, info := range infos {
    69  		if strings.Contains(info.FSType, "rclone") {
    70  			return nil
    71  		}
    72  	}
    73  
    74  	return fmt.Errorf(msg, mountpointAbs)
    75  }
    76  
    77  // CanCheckMountReady is set if CheckMountReady is functional
    78  var CanCheckMountReady = true