Split MajorMinor into two fields

this reflects what is in the docker/docker/pkg/mount API. makes it
easier to port additional code to out API.
This commit is contained in:
Davanum Srinivas 2020-01-21 10:28:19 -05:00 committed by Srini Brahmaroutu
parent 241ff06ccc
commit 0f9a3f756b
3 changed files with 134 additions and 13 deletions

View File

@ -61,8 +61,12 @@ type MountInfo struct {
ID int
// The ID of the parent mount (or of self for the root of this mount namespace's mount tree).
ParentID int
// The value of `st_dev` for files on this filesystem.
MajorMinor string
// Major indicates one half of the device ID which identifies the device class
// (parsed from `st_dev` for files on this filesystem).
Major int
// Minor indicates one half of the device ID which identifies a specific
// instance of device (parsed from `st_dev` for files on this filesystem).
Minor int
// The pathname of the directory in the filesystem which forms the root of this mount.
Root string
// Mount source, filesystem-specific information. e.g. device, tmpfs name.
@ -106,10 +110,24 @@ func ParseMountInfo(filename string) ([]MountInfo, error) {
if err != nil {
return nil, err
}
mm := strings.Split(fields[2], ":")
if len(mm) != 2 {
return nil, fmt.Errorf("parsing '%s' failed: unexpected minor:major pair %s", line, mm)
}
major, err := strconv.Atoi(mm[0])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: unable to parse major device id, err:%v", mm[0], err)
}
minor, err := strconv.Atoi(mm[1])
if err != nil {
return nil, fmt.Errorf("parsing '%s' failed: unable to parse minor device id, err:%v", mm[1], err)
}
info := MountInfo{
ID: id,
ParentID: parentID,
MajorMinor: fields[2],
Major: major,
Minor: minor,
Root: fields[3],
MountPoint: fields[4],
MountOptions: strings.Split(fields[5], ","),

View File

@ -100,7 +100,8 @@ func TestParseMountInfo(t *testing.T) {
MountInfo{
ID: 189,
ParentID: 80,
MajorMinor: "8:1",
Major: 8,
Minor: 1,
Root: "/var/lib/kubelet",
Source: "/dev/sda1",
MountPoint: "/var/lib/kubelet",
@ -116,7 +117,8 @@ func TestParseMountInfo(t *testing.T) {
MountInfo{
ID: 222,
ParentID: 24,
MajorMinor: "253:0",
Major: 253,
Minor: 0,
Root: "/tmp/src",
Source: "/dev/mapper/vagrant--vg-root",
MountPoint: "/mnt/dst",
@ -132,7 +134,8 @@ func TestParseMountInfo(t *testing.T) {
MountInfo{
ID: 224,
ParentID: 62,
MajorMinor: "253:0",
Major: 253,
Minor: 0,
Root: "/var/lib/docker/devicemapper/test/shared",
Source: "/dev/mapper/ssd-root",
MountPoint: "/var/lib/docker/devicemapper/test/shared",
@ -148,7 +151,8 @@ func TestParseMountInfo(t *testing.T) {
MountInfo{
ID: 28,
ParentID: 18,
MajorMinor: "0:24",
Major: 0,
Minor: 24,
Root: "/",
Source: "tmpfs",
MountPoint: "/sys/fs/cgroup",
@ -164,7 +168,8 @@ func TestParseMountInfo(t *testing.T) {
MountInfo{
ID: 29,
ParentID: 28,
MajorMinor: "0:25",
Major: 0,
Minor: 25,
Root: "/",
Source: "cgroup",
MountPoint: "/sys/fs/cgroup/systemd",
@ -180,7 +185,8 @@ func TestParseMountInfo(t *testing.T) {
MountInfo{
ID: 31,
ParentID: 28,
MajorMinor: "0:27",
Major: 0,
Minor: 27,
Root: "/",
Source: "cgroup",
MountPoint: "/sys/fs/cgroup/cpuset",
@ -213,3 +219,98 @@ func TestParseMountInfo(t *testing.T) {
}
}
}
func TestBadParseMountInfo(t *testing.T) {
tests := []struct {
info string
name string
id int
expectedInfo *MountInfo
error string
}{
{
`224 62 253:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`,
"good major:minor field",
224,
&MountInfo{
ID: 224,
ParentID: 62,
Major: 253,
Minor: 0,
Root: "/var/lib/docker/devicemapper/test/shared",
Source: "/dev/mapper/ssd-root",
MountPoint: "/var/lib/docker/devicemapper/test/shared",
OptionalFields: []string{"master:1", "shared:44"},
FsType: "ext4",
MountOptions: []string{"rw", "relatime"},
SuperOptions: []string{"rw", "seclabel", "data=ordered"},
},
"",
},
{
`224 62 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`,
"missing major:minor field",
224,
nil,
`parsing '224 62 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered' failed: unexpected minor:major pair [/var/lib/docker/devicemapper/test/shared]`,
},
{
`224 62 :0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`,
"empty major field",
224,
nil,
`parsing '' failed: unable to parse major device id, err:strconv.Atoi: parsing "": invalid syntax`,
},
{
`224 62 253: /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`,
"empty minor field",
224,
nil,
`parsing '' failed: unable to parse minor device id, err:strconv.Atoi: parsing "": invalid syntax`,
},
{
`224 62 foo:0 /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`,
"alphabet in major field",
224,
nil,
`parsing 'foo' failed: unable to parse major device id, err:strconv.Atoi: parsing "foo": invalid syntax`,
},
{
`224 62 253:bar /var/lib/docker/devicemapper/test/shared /var/lib/docker/devicemapper/test/shared rw,relatime master:1 shared:44 - ext4 /dev/mapper/ssd-root rw,seclabel,data=ordered`,
"alphabet in minor field",
224,
nil,
`parsing 'bar' failed: unable to parse minor device id, err:strconv.Atoi: parsing "bar": invalid syntax`,
},
}
for _, test := range tests {
tempDir, filename, err := writeFile(test.info)
if err != nil {
t.Fatalf("cannot create temporary file: %v", err)
}
defer os.RemoveAll(tempDir)
infos, err := ParseMountInfo(filename)
if err != nil {
if err.Error() != test.error {
t.Errorf("Test case %q:\n expected error: %+v\n got: %+v", test.name, test.error, err.Error())
}
continue
}
found := false
for _, info := range infos {
if info.ID == test.id {
found = true
if !reflect.DeepEqual(info, *test.expectedInfo) {
t.Errorf("Test case %q:\n expected: %+v\n got: %+v", test.name, test.expectedInfo, info)
}
break
}
}
if !found {
t.Errorf("Test case %q: mountPoint %d not found", test.name, test.id)
}
}
}

View File

@ -499,7 +499,8 @@ func SearchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
mountID := 0
rootPath := ""
majorMinor := ""
major := -1
minor := -1
// Finding the underlying root path and major:minor if possible.
// We need search in backward order because it's possible for later mounts
@ -509,12 +510,13 @@ func SearchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
// If it's a mount point or path under a mount point.
mountID = mis[i].ID
rootPath = filepath.Join(mis[i].Root, strings.TrimPrefix(hostSource, mis[i].MountPoint))
majorMinor = mis[i].MajorMinor
major = mis[i].Major
minor = mis[i].Minor
break
}
}
if rootPath == "" || majorMinor == "" {
if rootPath == "" || major == -1 || minor == -1 {
return nil, fmt.Errorf("failed to get root path and major:minor for %s", hostSource)
}
@ -524,7 +526,7 @@ func SearchMountPoints(hostSource, mountInfoPath string) ([]string, error) {
// Ignore mount entry for mount source itself.
continue
}
if mis[i].Root == rootPath && mis[i].MajorMinor == majorMinor {
if mis[i].Root == rootPath && mis[i].Major == major && mis[i].Minor == minor {
refs = append(refs, mis[i].MountPoint)
}
}