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 }