github.com/cdoern/storage@v1.12.13/pkg/mount/mount.go (about)

     1  package mount
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/containers/storage/pkg/fileutils"
     9  )
    10  
    11  // GetMounts retrieves a list of mounts for the current running process.
    12  func GetMounts() ([]*Info, error) {
    13  	return parseMountTable()
    14  }
    15  
    16  // Mounted determines if a specified mountpoint has been mounted.
    17  // On Linux it looks at /proc/self/mountinfo and on Solaris at mnttab.
    18  func Mounted(mountpoint string) (bool, error) {
    19  	entries, err := parseMountTable()
    20  	if err != nil {
    21  		return false, err
    22  	}
    23  
    24  	mountpoint, err = fileutils.ReadSymlinkedDirectory(mountpoint)
    25  	if err != nil {
    26  		return false, err
    27  	}
    28  	// Search the table for the mountpoint
    29  	for _, e := range entries {
    30  		if e.Mountpoint == mountpoint {
    31  			return true, nil
    32  		}
    33  	}
    34  	return false, nil
    35  }
    36  
    37  // Mount will mount filesystem according to the specified configuration, on the
    38  // condition that the target path is *not* already mounted. Options must be
    39  // specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
    40  // flags.go for supported option flags.
    41  func Mount(device, target, mType, options string) error {
    42  	flag, _ := ParseOptions(options)
    43  	if flag&REMOUNT != REMOUNT {
    44  		if mounted, err := Mounted(target); err != nil || mounted {
    45  			return err
    46  		}
    47  	}
    48  	return ForceMount(device, target, mType, options)
    49  }
    50  
    51  // ForceMount will mount a filesystem according to the specified configuration,
    52  // *regardless* if the target path is not already mounted. Options must be
    53  // specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
    54  // flags.go for supported option flags.
    55  func ForceMount(device, target, mType, options string) error {
    56  	flag, data := ParseOptions(options)
    57  	return mount(device, target, mType, uintptr(flag), data)
    58  }
    59  
    60  // Unmount lazily unmounts a filesystem on supported platforms, otherwise
    61  // does a normal unmount.
    62  func Unmount(target string) error {
    63  	if mounted, err := Mounted(target); err != nil || !mounted {
    64  		return err
    65  	}
    66  	return ForceUnmount(target)
    67  }
    68  
    69  // RecursiveUnmount unmounts the target and all mounts underneath, starting with
    70  // the deepsest mount first.
    71  func RecursiveUnmount(target string) error {
    72  	mounts, err := GetMounts()
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	// Make the deepest mount be first
    78  	sort.Sort(sort.Reverse(byMountpoint(mounts)))
    79  
    80  	for i, m := range mounts {
    81  		if !strings.HasPrefix(m.Mountpoint, target) {
    82  			continue
    83  		}
    84  		if err := Unmount(m.Mountpoint); err != nil && i == len(mounts)-1 {
    85  			if mounted, err := Mounted(m.Mountpoint); err != nil || mounted {
    86  				return err
    87  			}
    88  			// Ignore errors for submounts and continue trying to unmount others
    89  			// The final unmount should fail if there ane any submounts remaining
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  // ForceUnmount will force an unmount of the target filesystem, regardless if
    96  // it is mounted or not.
    97  func ForceUnmount(target string) (err error) {
    98  	// Simple retry logic for unmount
    99  	for i := 0; i < 10; i++ {
   100  		if err = unmount(target, 0); err == nil {
   101  			return nil
   102  		}
   103  		time.Sleep(100 * time.Millisecond)
   104  	}
   105  	return nil
   106  }