mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
getSourceMount(): simplify
The flow of getSourceMount was: 1 get all entries from /proc/self/mountinfo 2 do a linear search for the `source` directory 3 if found, return its data 4 get the parent directory of `source`, goto 2 The repeated linear search through the whole mountinfo (which can have thousands of records) is inefficient. Instead, let's just 1 collect all the relevant records (only those mount points that can be a parent of `source`) 2 find the record with the longest mountpath, return its data This was tested manually with something like ```go func TestGetSourceMount(t *testing.T) { mnt, flags, err := getSourceMount("/sys/devices/msr/") assert.NoError(t, err) t.Logf("mnt: %v, flags: %v", mnt, flags) } ``` ...but it relies on having a specific mount points on the system being used for testing. [v2: add unit tests for ParentsFilter] Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
parent
d3ebcde82a
commit
871c957242
3 changed files with 30 additions and 25 deletions
|
@ -380,15 +380,6 @@ func specMapping(s []idtools.IDMap) []specs.LinuxIDMapping {
|
||||||
return ids
|
return ids
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMountInfo(mountinfo []*mount.Info, dir string) *mount.Info {
|
|
||||||
for _, m := range mountinfo {
|
|
||||||
if m.Mountpoint == dir {
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the source mount point of directory passed in as argument. Also return
|
// Get the source mount point of directory passed in as argument. Also return
|
||||||
// optional fields.
|
// optional fields.
|
||||||
func getSourceMount(source string) (string, string, error) {
|
func getSourceMount(source string) (string, string, error) {
|
||||||
|
@ -398,28 +389,25 @@ func getSourceMount(source string) (string, string, error) {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
mountinfos, err := mount.GetMounts(nil)
|
mi, err := mount.GetMounts(mount.ParentsFilter(sourcePath))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
if len(mi) < 1 {
|
||||||
mountinfo := getMountInfo(mountinfos, sourcePath)
|
return "", "", fmt.Errorf("Can't find mount point of %s", source)
|
||||||
if mountinfo != nil {
|
|
||||||
return sourcePath, mountinfo.Optional, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path := sourcePath
|
// find the longest mount point
|
||||||
for {
|
var idx, maxlen int
|
||||||
path = filepath.Dir(path)
|
for i := range mi {
|
||||||
|
if len(mi[i].Mountpoint) > maxlen {
|
||||||
mountinfo = getMountInfo(mountinfos, path)
|
maxlen = len(mi[i].Mountpoint)
|
||||||
if mountinfo != nil {
|
idx = i
|
||||||
return path, mountinfo.Optional, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if path == "/" {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
// and return it unless it's "/"
|
||||||
|
if mi[idx].Mountpoint != "/" {
|
||||||
|
return mi[idx].Mountpoint, mi[idx].Optional, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we are here, we did not find parent mount. Something is wrong.
|
// If we are here, we did not find parent mount. Something is wrong.
|
||||||
|
|
|
@ -36,6 +36,17 @@ func SingleEntryFilter(mp string) FilterFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParentsFilter returns all entries whose mount points
|
||||||
|
// can be parents of a path specified, discarding others.
|
||||||
|
// For example, given `/var/lib/docker/something`, entries
|
||||||
|
// like `/var/lib/docker`, `/var` and `/` are returned.
|
||||||
|
func ParentsFilter(path string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
skip := !strings.HasPrefix(path, m.Mountpoint)
|
||||||
|
return skip, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// GetMounts retrieves a list of mounts for the current running process,
|
// GetMounts retrieves a list of mounts for the current running process,
|
||||||
// with an optional filter applied (use nil for no filter).
|
// with an optional filter applied (use nil for no filter).
|
||||||
func GetMounts(f FilterFunc) ([]*Info, error) {
|
func GetMounts(f FilterFunc) ([]*Info, error) {
|
||||||
|
|
|
@ -499,4 +499,10 @@ func TestParseMountinfoFilters(t *testing.T) {
|
||||||
infos, err = parseInfoFile(r, PrefixFilter("nonexistent"))
|
infos, err = parseInfoFile(r, PrefixFilter("nonexistent"))
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
assert.Equal(t, 0, len(infos))
|
assert.Equal(t, 0, len(infos))
|
||||||
|
|
||||||
|
r.Reset([]byte(fedoraMountinfo))
|
||||||
|
infos, err = parseInfoFile(r, ParentsFilter("/sys/fs/cgroup/cpu,cpuacct"))
|
||||||
|
assert.NilError(t, err)
|
||||||
|
// there should be 4 results returned: /sys/fs/cgroup/cpu,cpuacct /sys/fs/cgroup /sys /
|
||||||
|
assert.Equal(t, 4, len(infos))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue