1
2
3
4
5
6
7
8
9
10
11
12
13
14 package procfs
15
16 import (
17 "bufio"
18 "bytes"
19 "fmt"
20 "strconv"
21 "strings"
22
23 "github.com/prometheus/procfs/internal/util"
24 )
25
26
27
28
29
30
31 type MountInfo struct {
32
33 MountID int
34
35 ParentID int
36
37 MajorMinorVer string
38
39
40 Root string
41
42 MountPoint string
43
44 Options map[string]string
45
46 OptionalFields map[string]string
47
48 FSType string
49
50 Source string
51
52 SuperOptions map[string]string
53 }
54
55
56 func parseMountInfo(info []byte) ([]*MountInfo, error) {
57 mounts := []*MountInfo{}
58 scanner := bufio.NewScanner(bytes.NewReader(info))
59 for scanner.Scan() {
60 mountString := scanner.Text()
61 parsedMounts, err := parseMountInfoString(mountString)
62 if err != nil {
63 return nil, err
64 }
65 mounts = append(mounts, parsedMounts)
66 }
67
68 err := scanner.Err()
69 return mounts, err
70 }
71
72
73
74
75 func parseMountInfoString(mountString string) (*MountInfo, error) {
76 var err error
77
78 mountInfo := strings.Split(mountString, " ")
79 mountInfoLength := len(mountInfo)
80 if mountInfoLength < 10 {
81 return nil, fmt.Errorf("%w: Too few fields in mount string: %s", ErrFileParse, mountString)
82 }
83
84 if mountInfo[mountInfoLength-4] != "-" {
85 return nil, fmt.Errorf("%w: couldn't find separator in expected field: %s", ErrFileParse, mountInfo[mountInfoLength-4])
86 }
87
88 mount := &MountInfo{
89 MajorMinorVer: mountInfo[2],
90 Root: mountInfo[3],
91 MountPoint: mountInfo[4],
92 Options: mountOptionsParser(mountInfo[5]),
93 OptionalFields: nil,
94 FSType: mountInfo[mountInfoLength-3],
95 Source: mountInfo[mountInfoLength-2],
96 SuperOptions: mountOptionsParser(mountInfo[mountInfoLength-1]),
97 }
98
99 mount.MountID, err = strconv.Atoi(mountInfo[0])
100 if err != nil {
101 return nil, fmt.Errorf("%w: mount ID: %q", ErrFileParse, mount.MountID)
102 }
103 mount.ParentID, err = strconv.Atoi(mountInfo[1])
104 if err != nil {
105 return nil, fmt.Errorf("%w: parent ID: %q", ErrFileParse, mount.ParentID)
106 }
107
108
109 if mountInfo[6] != "" {
110 mount.OptionalFields, err = mountOptionsParseOptionalFields(mountInfo[6 : mountInfoLength-4])
111 if err != nil {
112 return nil, fmt.Errorf("%s: %w", ErrFileParse, err)
113 }
114 }
115 return mount, nil
116 }
117
118
119 func mountOptionsIsValidField(s string) bool {
120 switch s {
121 case
122 "shared",
123 "master",
124 "propagate_from",
125 "unbindable":
126 return true
127 }
128 return false
129 }
130
131
132 func mountOptionsParseOptionalFields(o []string) (map[string]string, error) {
133 optionalFields := make(map[string]string)
134 for _, field := range o {
135 optionSplit := strings.SplitN(field, ":", 2)
136 value := ""
137 if len(optionSplit) == 2 {
138 value = optionSplit[1]
139 }
140 if mountOptionsIsValidField(optionSplit[0]) {
141 optionalFields[optionSplit[0]] = value
142 }
143 }
144 return optionalFields, nil
145 }
146
147
148 func mountOptionsParser(mountOptions string) map[string]string {
149 opts := make(map[string]string)
150 options := strings.Split(mountOptions, ",")
151 for _, opt := range options {
152 splitOption := strings.Split(opt, "=")
153 if len(splitOption) < 2 {
154 key := splitOption[0]
155 opts[key] = ""
156 } else {
157 key, value := splitOption[0], splitOption[1]
158 opts[key] = value
159 }
160 }
161 return opts
162 }
163
164
165 func GetMounts() ([]*MountInfo, error) {
166 data, err := util.ReadFileNoStat("/proc/self/mountinfo")
167 if err != nil {
168 return nil, err
169 }
170 return parseMountInfo(data)
171 }
172
173
174 func GetProcMounts(pid int) ([]*MountInfo, error) {
175 data, err := util.ReadFileNoStat(fmt.Sprintf("/proc/%d/mountinfo", pid))
176 if err != nil {
177 return nil, err
178 }
179 return parseMountInfo(data)
180 }
181
View as plain text