github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/pkg/mount/mount.go (about)

     1  package mount // import "github.com/docker/docker/pkg/mount"
     2  
     3  import (
     4  	"sort"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/sirupsen/logrus"
     9  )
    10  
    11  // mountError records an error from mount or unmount operation
    12  type mountError struct {
    13  	op             string
    14  	source, target string
    15  	flags          uintptr
    16  	data           string
    17  	err            error
    18  }
    19  
    20  func (e *mountError) Error() string {
    21  	out := e.op + " "
    22  
    23  	if e.source != "" {
    24  		out += e.source + ":" + e.target
    25  	} else {
    26  		out += e.target
    27  	}
    28  
    29  	if e.flags != uintptr(0) {
    30  		out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
    31  	}
    32  	if e.data != "" {
    33  		out += ", data: " + e.data
    34  	}
    35  
    36  	out += ": " + e.err.Error()
    37  	return out
    38  }
    39  
    40  // Cause returns the underlying cause of the error
    41  func (e *mountError) Cause() error {
    42  	return e.err
    43  }
    44  
    45  // FilterFunc is a type defining a callback function
    46  // to filter out unwanted entries. It takes a pointer
    47  // to an Info struct (not fully populated, currently
    48  // only Mountpoint is filled in), and returns two booleans:
    49  //  - skip: true if the entry should be skipped
    50  //  - stop: true if parsing should be stopped after the entry
    51  type FilterFunc func(*Info) (skip, stop bool)
    52  
    53  // PrefixFilter discards all entries whose mount points
    54  // do not start with a prefix specified
    55  func PrefixFilter(prefix string) FilterFunc {
    56  	return func(m *Info) (bool, bool) {
    57  		skip := !strings.HasPrefix(m.Mountpoint, prefix)
    58  		return skip, false
    59  	}
    60  }
    61  
    62  // SingleEntryFilter looks for a specific entry
    63  func SingleEntryFilter(mp string) FilterFunc {
    64  	return func(m *Info) (bool, bool) {
    65  		if m.Mountpoint == mp {
    66  			return false, true // don't skip, stop now
    67  		}
    68  		return true, false // skip, keep going
    69  	}
    70  }
    71  
    72  // ParentsFilter returns all entries whose mount points
    73  // can be parents of a path specified, discarding others.
    74  // For example, given `/var/lib/docker/something`, entries
    75  // like `/var/lib/docker`, `/var` and `/` are returned.
    76  func ParentsFilter(path string) FilterFunc {
    77  	return func(m *Info) (bool, bool) {
    78  		skip := !strings.HasPrefix(path, m.Mountpoint)
    79  		return skip, false
    80  	}
    81  }
    82  
    83  // GetMounts retrieves a list of mounts for the current running process,
    84  // with an optional filter applied (use nil for no filter).
    85  func GetMounts(f FilterFunc) ([]*Info, error) {
    86  	return parseMountTable(f)
    87  }
    88  
    89  // Mounted determines if a specified mountpoint has been mounted.
    90  // On Linux it looks at /proc/self/mountinfo.
    91  func Mounted(mountpoint string) (bool, error) {
    92  	entries, err := GetMounts(SingleEntryFilter(mountpoint))
    93  	if err != nil {
    94  		return false, err
    95  	}
    96  
    97  	return len(entries) > 0, nil
    98  }
    99  
   100  // Mount will mount filesystem according to the specified configuration, on the
   101  // condition that the target path is *not* already mounted. Options must be
   102  // specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
   103  // flags.go for supported option flags.
   104  func Mount(device, target, mType, options string) error {
   105  	flag, _ := parseOptions(options)
   106  	if flag&REMOUNT != REMOUNT {
   107  		if mounted, err := Mounted(target); err != nil || mounted {
   108  			return err
   109  		}
   110  	}
   111  	return ForceMount(device, target, mType, options)
   112  }
   113  
   114  // ForceMount will mount a filesystem according to the specified configuration,
   115  // *regardless* if the target path is not already mounted. Options must be
   116  // specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
   117  // flags.go for supported option flags.
   118  func ForceMount(device, target, mType, options string) error {
   119  	flag, data := parseOptions(options)
   120  	return mount(device, target, mType, uintptr(flag), data)
   121  }
   122  
   123  // Unmount lazily unmounts a filesystem on supported platforms, otherwise
   124  // does a normal unmount.
   125  func Unmount(target string) error {
   126  	return unmount(target, mntDetach)
   127  }
   128  
   129  // RecursiveUnmount unmounts the target and all mounts underneath, starting with
   130  // the deepsest mount first.
   131  func RecursiveUnmount(target string) error {
   132  	mounts, err := parseMountTable(PrefixFilter(target))
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	// Make the deepest mount be first
   138  	sort.Slice(mounts, func(i, j int) bool {
   139  		return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
   140  	})
   141  
   142  	for i, m := range mounts {
   143  		logrus.Debugf("Trying to unmount %s", m.Mountpoint)
   144  		err = unmount(m.Mountpoint, mntDetach)
   145  		if err != nil {
   146  			if i == len(mounts)-1 { // last mount
   147  				if mounted, e := Mounted(m.Mountpoint); e != nil || mounted {
   148  					return err
   149  				}
   150  			} else {
   151  				// This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem
   152  				logrus.WithError(err).Warnf("Failed to unmount submount %s", m.Mountpoint)
   153  			}
   154  		}
   155  
   156  		logrus.Debugf("Unmounted %s", m.Mountpoint)
   157  	}
   158  	return nil
   159  }