Merge pull request #1861 from shirou/feat/fix_paritions_opts_split

[disk][linux]: fix parsing mount option when use 1/mounts on Partition
fix/aix_host_comma
shirou 4 weeks ago committed by GitHub
commit afdd6f1f91
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -285,81 +285,101 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
return nil, err return nil, err
} }
ret := make([]PartitionStat, 0, len(lines)) var ret []PartitionStat
if useMounts { // use mounts file
ret = parseFieldsOnMounts(lines, all, fs)
return ret, nil
}
// use mountinfo
ret, err = parseFieldsOnMountinfo(ctx, lines, all, fs, filename)
if err != nil {
return nil, fmt.Errorf("error parsing mountinfo file %s: %w", filename, err)
}
return ret, nil
}
func parseFieldsOnMounts(lines []string, all bool, fs []string) []PartitionStat {
ret := make([]PartitionStat, 0, len(lines))
for _, line := range lines { for _, line := range lines {
var d PartitionStat fields := strings.Fields(line)
if useMounts {
fields := strings.Fields(line)
d = PartitionStat{
Device: fields[0],
Mountpoint: unescapeFstab(fields[1]),
Fstype: fields[2],
Opts: strings.Fields(fields[3]),
}
if !all { d := PartitionStat{
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) { Device: fields[0],
continue Mountpoint: unescapeFstab(fields[1]),
} Fstype: fields[2],
} Opts: strings.Split(fields[3], ","),
} else { }
// a line of 1/mountinfo has the following structure:
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue if !all {
// (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
continue
// split the mountinfo line by the separator hyphen
parts := strings.Split(line, " - ")
if len(parts) != 2 {
return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line)
} }
}
ret = append(ret, d)
}
fields := strings.Fields(parts[0]) return ret
blockDeviceID := fields[2] }
mountPoint := fields[4]
mountOpts := strings.Split(fields[5], ",")
if rootDir := fields[3]; rootDir != "" && rootDir != "/" { func parseFieldsOnMountinfo(ctx context.Context, lines []string, all bool, fs []string, filename string) ([]PartitionStat, error) {
mountOpts = append(mountOpts, "bind") ret := make([]PartitionStat, 0, len(lines))
}
fields = strings.Fields(parts[1]) for _, line := range lines {
fstype := fields[0] // a line of 1/mountinfo has the following structure:
device := fields[1] // 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
// (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11)
// split the mountinfo line by the separator hyphen
parts := strings.Split(line, " - ")
if len(parts) != 2 {
return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line)
}
d = PartitionStat{ fields := strings.Fields(parts[0])
Device: device, blockDeviceID := fields[2]
Mountpoint: unescapeFstab(mountPoint), mountPoint := fields[4]
Fstype: fstype, mountOpts := strings.Split(fields[5], ",")
Opts: mountOpts,
}
if !all { if rootDir := fields[3]; rootDir != "" && rootDir != "/" {
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) { mountOpts = append(mountOpts, "bind")
continue }
}
fields = strings.Fields(parts[1])
fstype := fields[0]
device := fields[1]
d := PartitionStat{
Device: device,
Mountpoint: unescapeFstab(mountPoint),
Fstype: fstype,
Opts: mountOpts,
}
if !all {
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
continue
} }
}
if strings.HasPrefix(d.Device, "/dev/mapper/") { if strings.HasPrefix(d.Device, "/dev/mapper/") {
devpath, err := filepath.EvalSymlinks(common.HostDevWithContext(ctx, strings.Replace(d.Device, "/dev", "", 1))) devpath, err := filepath.EvalSymlinks(common.HostDevWithContext(ctx, strings.Replace(d.Device, "/dev", "", 1)))
if err == nil { if err == nil {
d.Device = devpath d.Device = devpath
}
} }
}
// /dev/root is not the real device name // /dev/root is not the real device name
// so we get the real device name from its major/minor number // so we get the real device name from its major/minor number
if d.Device == "/dev/root" { if d.Device == "/dev/root" {
devpath, err := os.Readlink(common.HostSysWithContext(ctx, "/dev/block/"+blockDeviceID)) devpath, err := os.Readlink(common.HostSysWithContext(ctx, "/dev/block/"+blockDeviceID))
if err == nil { if err == nil {
d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1) d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1)
}
} }
} }
ret = append(ret, d) ret = append(ret, d)
} }
return ret, nil return ret, nil
} }

@ -0,0 +1,83 @@
// SPDX-License-Identifier: BSD-3-Clause
//go:build linux
package disk
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_parseFieldsOnMountinfo(t *testing.T) {
fs := []string{"sysfs", "tmpfs"}
lines := []string{
"111 80 0:22 / /sys rw,nosuid,nodev,noexec,noatime shared:15 - sysfs sysfs rw",
"114 80 0:61 / /run rw,nosuid,nodev shared:18 - tmpfs none rw,mode=755",
}
cases := map[string]struct {
all bool
expect []PartitionStat
}{
"all": {
all: true,
expect: []PartitionStat{
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
{Device: "none", Mountpoint: "/run", Fstype: "tmpfs", Opts: []string{"rw", "nosuid", "nodev"}},
},
},
"not all": {
all: false,
expect: []PartitionStat{
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
},
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
actual, err := parseFieldsOnMountinfo(context.Background(), lines, c.all, fs, "")
require.NoError(t, err)
assert.Equal(t, c.expect, actual)
})
}
}
func Test_parseFieldsOnMounts(t *testing.T) {
fs := []string{"sysfs", "tmpfs"}
lines := []string{
"sysfs /sys sysfs rw,nosuid,nodev,noexec,noatime 0 0",
"none /run tmpfs rw,nosuid,nodev,mode=755 0 0",
}
cases := map[string]struct {
all bool
expect []PartitionStat
}{
"all": {
all: true,
expect: []PartitionStat{
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
{Device: "none", Mountpoint: "/run", Fstype: "tmpfs", Opts: []string{"rw", "nosuid", "nodev", "mode=755"}},
},
},
"not all": {
all: false,
expect: []PartitionStat{
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
},
},
}
for name, c := range cases {
t.Run(name, func(t *testing.T) {
actual := parseFieldsOnMounts(lines, c.all, fs)
assert.Equal(t, c.expect, actual)
})
}
}
Loading…
Cancel
Save