Skip to content

Commit afdd6f1

Browse files
authored
Merge pull request #1861 from shirou/feat/fix_paritions_opts_split
[disk][linux]: fix parsing mount option when use 1/mounts on Partition
2 parents 15412ac + d3578cf commit afdd6f1

File tree

2 files changed

+161
-58
lines changed

2 files changed

+161
-58
lines changed

disk/disk_linux.go

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -285,81 +285,101 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro
285285
return nil, err
286286
}
287287

288-
ret := make([]PartitionStat, 0, len(lines))
288+
var ret []PartitionStat
289+
if useMounts { // use mounts file
290+
ret = parseFieldsOnMounts(lines, all, fs)
291+
return ret, nil
292+
}
293+
294+
// use mountinfo
295+
ret, err = parseFieldsOnMountinfo(ctx, lines, all, fs, filename)
296+
if err != nil {
297+
return nil, fmt.Errorf("error parsing mountinfo file %s: %w", filename, err)
298+
}
299+
300+
return ret, nil
301+
}
289302

303+
func parseFieldsOnMounts(lines []string, all bool, fs []string) []PartitionStat {
304+
ret := make([]PartitionStat, 0, len(lines))
290305
for _, line := range lines {
291-
var d PartitionStat
292-
if useMounts {
293-
fields := strings.Fields(line)
294-
295-
d = PartitionStat{
296-
Device: fields[0],
297-
Mountpoint: unescapeFstab(fields[1]),
298-
Fstype: fields[2],
299-
Opts: strings.Fields(fields[3]),
300-
}
306+
fields := strings.Fields(line)
301307

302-
if !all {
303-
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
304-
continue
305-
}
306-
}
307-
} else {
308-
// a line of 1/mountinfo has the following structure:
309-
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
310-
// (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11)
311-
312-
// split the mountinfo line by the separator hyphen
313-
parts := strings.Split(line, " - ")
314-
if len(parts) != 2 {
315-
return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line)
308+
d := PartitionStat{
309+
Device: fields[0],
310+
Mountpoint: unescapeFstab(fields[1]),
311+
Fstype: fields[2],
312+
Opts: strings.Split(fields[3], ","),
313+
}
314+
315+
if !all {
316+
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
317+
continue
316318
}
319+
}
320+
ret = append(ret, d)
321+
}
317322

318-
fields := strings.Fields(parts[0])
319-
blockDeviceID := fields[2]
320-
mountPoint := fields[4]
321-
mountOpts := strings.Split(fields[5], ",")
323+
return ret
324+
}
322325

323-
if rootDir := fields[3]; rootDir != "" && rootDir != "/" {
324-
mountOpts = append(mountOpts, "bind")
325-
}
326+
func parseFieldsOnMountinfo(ctx context.Context, lines []string, all bool, fs []string, filename string) ([]PartitionStat, error) {
327+
ret := make([]PartitionStat, 0, len(lines))
326328

327-
fields = strings.Fields(parts[1])
328-
fstype := fields[0]
329-
device := fields[1]
329+
for _, line := range lines {
330+
// a line of 1/mountinfo has the following structure:
331+
// 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue
332+
// (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11)
333+
334+
// split the mountinfo line by the separator hyphen
335+
parts := strings.Split(line, " - ")
336+
if len(parts) != 2 {
337+
return nil, fmt.Errorf("found invalid mountinfo line in file %s: %s ", filename, line)
338+
}
330339

331-
d = PartitionStat{
332-
Device: device,
333-
Mountpoint: unescapeFstab(mountPoint),
334-
Fstype: fstype,
335-
Opts: mountOpts,
336-
}
340+
fields := strings.Fields(parts[0])
341+
blockDeviceID := fields[2]
342+
mountPoint := fields[4]
343+
mountOpts := strings.Split(fields[5], ",")
337344

338-
if !all {
339-
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
340-
continue
341-
}
345+
if rootDir := fields[3]; rootDir != "" && rootDir != "/" {
346+
mountOpts = append(mountOpts, "bind")
347+
}
348+
349+
fields = strings.Fields(parts[1])
350+
fstype := fields[0]
351+
device := fields[1]
352+
353+
d := PartitionStat{
354+
Device: device,
355+
Mountpoint: unescapeFstab(mountPoint),
356+
Fstype: fstype,
357+
Opts: mountOpts,
358+
}
359+
360+
if !all {
361+
if d.Device == "none" || !common.StringsHas(fs, d.Fstype) {
362+
continue
342363
}
364+
}
343365

344-
if strings.HasPrefix(d.Device, "/dev/mapper/") {
345-
devpath, err := filepath.EvalSymlinks(common.HostDevWithContext(ctx, strings.Replace(d.Device, "/dev", "", 1)))
346-
if err == nil {
347-
d.Device = devpath
348-
}
366+
if strings.HasPrefix(d.Device, "/dev/mapper/") {
367+
devpath, err := filepath.EvalSymlinks(common.HostDevWithContext(ctx, strings.Replace(d.Device, "/dev", "", 1)))
368+
if err == nil {
369+
d.Device = devpath
349370
}
371+
}
350372

351-
// /dev/root is not the real device name
352-
// so we get the real device name from its major/minor number
353-
if d.Device == "/dev/root" {
354-
devpath, err := os.Readlink(common.HostSysWithContext(ctx, "/dev/block/"+blockDeviceID))
355-
if err == nil {
356-
d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1)
357-
}
373+
// /dev/root is not the real device name
374+
// so we get the real device name from its major/minor number
375+
if d.Device == "/dev/root" {
376+
devpath, err := os.Readlink(common.HostSysWithContext(ctx, "/dev/block/"+blockDeviceID))
377+
if err == nil {
378+
d.Device = strings.Replace(d.Device, "root", filepath.Base(devpath), 1)
358379
}
359380
}
360381
ret = append(ret, d)
361382
}
362-
363383
return ret, nil
364384
}
365385

disk/disk_linux_test.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
//go:build linux
3+
4+
package disk
5+
6+
import (
7+
"context"
8+
"testing"
9+
10+
"github.com/stretchr/testify/assert"
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func Test_parseFieldsOnMountinfo(t *testing.T) {
15+
fs := []string{"sysfs", "tmpfs"}
16+
17+
lines := []string{
18+
"111 80 0:22 / /sys rw,nosuid,nodev,noexec,noatime shared:15 - sysfs sysfs rw",
19+
"114 80 0:61 / /run rw,nosuid,nodev shared:18 - tmpfs none rw,mode=755",
20+
}
21+
22+
cases := map[string]struct {
23+
all bool
24+
expect []PartitionStat
25+
}{
26+
"all": {
27+
all: true,
28+
expect: []PartitionStat{
29+
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
30+
{Device: "none", Mountpoint: "/run", Fstype: "tmpfs", Opts: []string{"rw", "nosuid", "nodev"}},
31+
},
32+
},
33+
"not all": {
34+
all: false,
35+
expect: []PartitionStat{
36+
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
37+
},
38+
},
39+
}
40+
41+
for name, c := range cases {
42+
t.Run(name, func(t *testing.T) {
43+
actual, err := parseFieldsOnMountinfo(context.Background(), lines, c.all, fs, "")
44+
require.NoError(t, err)
45+
assert.Equal(t, c.expect, actual)
46+
})
47+
}
48+
}
49+
50+
func Test_parseFieldsOnMounts(t *testing.T) {
51+
fs := []string{"sysfs", "tmpfs"}
52+
53+
lines := []string{
54+
"sysfs /sys sysfs rw,nosuid,nodev,noexec,noatime 0 0",
55+
"none /run tmpfs rw,nosuid,nodev,mode=755 0 0",
56+
}
57+
58+
cases := map[string]struct {
59+
all bool
60+
expect []PartitionStat
61+
}{
62+
"all": {
63+
all: true,
64+
expect: []PartitionStat{
65+
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
66+
{Device: "none", Mountpoint: "/run", Fstype: "tmpfs", Opts: []string{"rw", "nosuid", "nodev", "mode=755"}},
67+
},
68+
},
69+
"not all": {
70+
all: false,
71+
expect: []PartitionStat{
72+
{Device: "sysfs", Mountpoint: "/sys", Fstype: "sysfs", Opts: []string{"rw", "nosuid", "nodev", "noexec", "noatime"}},
73+
},
74+
},
75+
}
76+
77+
for name, c := range cases {
78+
t.Run(name, func(t *testing.T) {
79+
actual := parseFieldsOnMounts(lines, c.all, fs)
80+
assert.Equal(t, c.expect, actual)
81+
})
82+
}
83+
}

0 commit comments

Comments
 (0)