Skip to content

Commit 9d65a76

Browse files
committed
filesystem: Fix mountinfo parsing
Migrate mountinfo parsing to procfs library. This fixes incorrect parsing introduced by PR #3387. Fixes: #3450 Signed-off-by: Ben Kochie <superq@gmail.com>
1 parent 128a9ad commit 9d65a76

File tree

4 files changed

+43
-34
lines changed

4 files changed

+43
-34
lines changed

collector/filesystem_linux.go

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717
package collector
1818

1919
import (
20-
"bufio"
20+
"bytes"
2121
"errors"
2222
"fmt"
23-
"io"
2423
"log/slog"
2524
"os"
2625
"slices"
@@ -182,9 +181,9 @@ func stuckMountWatcher(mountPoint string, success chan struct{}, logger *slog.Lo
182181

183182
func mountPointDetails(logger *slog.Logger) ([]filesystemLabels, error) {
184183
fs, err := procfs.NewFS(*procPath)
185-
if err != nil {
186-
return nil, fmt.Errorf("failed to open procfs: %w", err)
187-
}
184+
if err != nil {
185+
return nil, fmt.Errorf("failed to open procfs: %w", err)
186+
}
188187
mountInfo, err := fs.GetProcMounts(1)
189188
if errors.Is(err, os.ErrNotExist) {
190189
// Fallback to `/proc/self/mountinfo` if `/proc/1/mountinfo` is missing due hidepid.
@@ -195,48 +194,37 @@ func mountPointDetails(logger *slog.Logger) ([]filesystemLabels, error) {
195194
return nil, err
196195
}
197196

198-
return parseFilesystemLabels(file)
197+
return parseFilesystemLabels(mountInfo)
199198
}
200199

201200
func parseFilesystemLabels(mountInfo []*procfs.MountInfo) ([]filesystemLabels, error) {
202201
var filesystems []filesystemLabels
203202

204-
for mount in range mountInfo {
205-
parts := strings.Fields(scanner.Text())
206-
207-
if len(parts) < 11 {
208-
return nil, fmt.Errorf("malformed mount point information: %q", scanner.Text())
209-
}
210-
203+
for _, mount := range mountInfo {
211204
major, minor := 0, 0
212-
_, err := fmt.Sscanf(parts[2], "%d:%d", &major, &minor)
205+
_, err := fmt.Sscanf(mount.MajorMinorVer, "%d:%d", &major, &minor)
213206
if err != nil {
214-
return nil, fmt.Errorf("malformed mount point information: %q", scanner.Text())
215-
}
216-
217-
m := 5
218-
for parts[m+1] != "-" {
219-
m++
207+
return nil, fmt.Errorf("malformed mount point MajorMinorVer: %q", mount.MajorMinorVer)
220208
}
221209

222210
// Ensure we handle the translation of \040 and \011
223211
// as per fstab(5).
224-
parts[4] = strings.ReplaceAll(parts[4], "\\040", " ")
225-
parts[4] = strings.ReplaceAll(parts[4], "\\011", "\t")
212+
mount.MountPoint = strings.ReplaceAll(mount.MountPoint, "\\040", " ")
213+
mount.MountPoint = strings.ReplaceAll(mount.MountPoint, "\\011", "\t")
226214

227215
filesystems = append(filesystems, filesystemLabels{
228-
device: parts[m+3],
229-
mountPoint: rootfsStripPrefix(parts[4]),
230-
fsType: parts[m+2],
231-
mountOptions: parts[5],
232-
superOptions: parts[10],
216+
device: mount.Source,
217+
mountPoint: rootfsStripPrefix(mount.MountPoint),
218+
fsType: mount.FSType,
219+
mountOptions: mountOptionsString(mount.Options),
220+
superOptions: mountOptionsString(mount.SuperOptions),
233221
major: strconv.Itoa(major),
234222
minor: strconv.Itoa(minor),
235223
deviceError: "",
236224
})
237225
}
238226

239-
return filesystems, scanner.Err()
227+
return filesystems, nil
240228
}
241229

242230
// see https://github.com/prometheus/node_exporter/issues/3157#issuecomment-2422761187
@@ -248,3 +236,15 @@ func isFilesystemReadOnly(labels filesystemLabels) bool {
248236

249237
return false
250238
}
239+
240+
func mountOptionsString(m map[string]string) string {
241+
b := new(bytes.Buffer)
242+
for key, value := range m {
243+
if value == "" {
244+
fmt.Fprintf(b, "%s", key)
245+
} else {
246+
fmt.Fprintf(b, "%s=%s", key, value)
247+
}
248+
}
249+
return b.String()
250+
}

collector/filesystem_linux_test.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,31 @@ package collector
1919
import (
2020
"io"
2121
"log/slog"
22-
"strings"
2322
"testing"
2423

2524
"github.com/alecthomas/kingpin/v2"
25+
26+
"github.com/prometheus/procfs"
2627
)
2728

2829
func Test_parseFilesystemLabelsError(t *testing.T) {
2930
tests := []struct {
3031
name string
31-
in string
32+
in []*procfs.MountInfo
3233
}{
3334
{
34-
name: "too few fields",
35-
in: "hello world",
35+
name: "malformed Major:Minor",
36+
in: []*procfs.MountInfo{
37+
{
38+
MajorMinorVer: "nope",
39+
},
40+
},
3641
},
3742
}
3843

3944
for _, tt := range tests {
4045
t.Run(tt.name, func(t *testing.T) {
41-
if _, err := parseFilesystemLabels(strings.NewReader(tt.in)); err == nil {
46+
if _, err := parseFilesystemLabels(tt.in); err == nil {
4247
t.Fatal("expected an error, but none occurred")
4348
}
4449
})

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ require (
2525
github.com/prometheus/client_model v0.6.2
2626
github.com/prometheus/common v0.67.1
2727
github.com/prometheus/exporter-toolkit v0.14.1
28-
github.com/prometheus/procfs v0.18.0
28+
github.com/prometheus/procfs v0.18.1-0.20251025084418-43b7c7448da2
2929
github.com/safchain/ethtool v0.6.2
3030
golang.org/x/exp v0.0.0-20250911091902-df9299821621
3131
golang.org/x/sys v0.37.0

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,12 @@ github.com/prometheus/common v0.67.1 h1:OTSON1P4DNxzTg4hmKCc37o4ZAZDv0cfXLkOt0oE
8080
github.com/prometheus/common v0.67.1/go.mod h1:RpmT9v35q2Y+lsieQsdOh5sXZ6ajUGC8NjZAmr8vb0Q=
8181
github.com/prometheus/exporter-toolkit v0.14.1 h1:uKPE4ewweVRWFainwvAcHs3uw15pjw2dk3I7b+aNo9o=
8282
github.com/prometheus/exporter-toolkit v0.14.1/go.mod h1:di7yaAJiaMkcjcz48f/u4yRPwtyuxTU5Jr4EnM2mhtQ=
83+
github.com/prometheus/procfs v0.17.1-0.20251025084002-e60b46b3da45 h1:/y4YcGcWAwmgZWxMsVWBZRBbPIJ+sxgXenRUvITCnd8=
84+
github.com/prometheus/procfs v0.17.1-0.20251025084002-e60b46b3da45/go.mod h1:sMb3/7ihkhO3ZKptrvInZq/aFyCqq7Y1/FJrHCY+hBE=
8385
github.com/prometheus/procfs v0.18.0 h1:2QTA9cKdznfYJz7EDaa7IiJobHuV7E1WzeBwcrhk0ao=
8486
github.com/prometheus/procfs v0.18.0/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
87+
github.com/prometheus/procfs v0.18.1-0.20251025084418-43b7c7448da2 h1:eHSoxAQdihm9FU0OxMEV4f766bcbS63L8eOYurtNp6E=
88+
github.com/prometheus/procfs v0.18.1-0.20251025084418-43b7c7448da2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
8589
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
8690
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
8791
github.com/safchain/ethtool v0.6.2 h1:O3ZPFAKEUEfbtE6J/feEe2Ft7dIJ2Sy8t4SdMRiIMHY=

0 commit comments

Comments
 (0)