|
19 | 19 | package mount |
20 | 20 |
|
21 | 21 | import ( |
22 | | - "bufio" |
23 | | - "fmt" |
24 | | - "io" |
25 | | - "os" |
26 | | - "strconv" |
27 | | - "strings" |
28 | | - |
29 | | - "github.com/pkg/errors" |
| 22 | + "github.com/moby/sys/mountinfo" |
30 | 23 | ) |
31 | 24 |
|
32 | 25 | // Self retrieves a list of mounts for the current running process. |
33 | 26 | func Self() ([]Info, error) { |
34 | | - f, err := os.Open("/proc/self/mountinfo") |
35 | | - if err != nil { |
36 | | - return nil, err |
37 | | - } |
38 | | - defer f.Close() |
39 | | - |
40 | | - return parseInfoFile(f) |
41 | | -} |
42 | | - |
43 | | -func parseInfoFile(r io.Reader) ([]Info, error) { |
44 | | - s := bufio.NewScanner(r) |
45 | | - out := []Info{} |
46 | | - var err error |
47 | | - for s.Scan() { |
48 | | - /* |
49 | | - See http://man7.org/linux/man-pages/man5/proc.5.html |
50 | | -
|
51 | | - 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue |
52 | | - (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) |
53 | | - (1) mount ID: unique identifier of the mount (may be reused after umount) |
54 | | - (2) parent ID: ID of parent (or of self for the top of the mount tree) |
55 | | - (3) major:minor: value of st_dev for files on filesystem |
56 | | - (4) root: root of the mount within the filesystem |
57 | | - (5) mount point: mount point relative to the process's root |
58 | | - (6) mount options: per mount options |
59 | | - (7) optional fields: zero or more fields of the form "tag[:value]" |
60 | | - (8) separator: marks the end of the optional fields |
61 | | - (9) filesystem type: name of filesystem of the form "type[.subtype]" |
62 | | - (10) mount source: filesystem specific information or "none" |
63 | | - (11) super options: per super block options |
64 | | - */ |
65 | | - |
66 | | - text := s.Text() |
67 | | - fields := strings.Split(text, " ") |
68 | | - numFields := len(fields) |
69 | | - if numFields < 10 { |
70 | | - // should be at least 10 fields |
71 | | - return nil, errors.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) |
72 | | - } |
73 | | - p := Info{} |
74 | | - // ignore any numbers parsing errors, as there should not be any |
75 | | - p.ID, _ = strconv.Atoi(fields[0]) |
76 | | - p.Parent, _ = strconv.Atoi(fields[1]) |
77 | | - mm := strings.Split(fields[2], ":") |
78 | | - if len(mm) != 2 { |
79 | | - return nil, errors.Errorf("parsing '%s' failed: unexpected minor:major pair %s", text, mm) |
80 | | - } |
81 | | - p.Major, _ = strconv.Atoi(mm[0]) |
82 | | - p.Minor, _ = strconv.Atoi(mm[1]) |
83 | | - |
84 | | - p.Root, err = strconv.Unquote(`"` + strings.Replace(fields[3], `"`, `\"`, -1) + `"`) |
85 | | - if err != nil { |
86 | | - return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote root field", fields[3]) |
87 | | - } |
88 | | - p.Mountpoint, err = strconv.Unquote(`"` + strings.Replace(fields[4], `"`, `\"`, -1) + `"`) |
89 | | - if err != nil { |
90 | | - return nil, errors.Wrapf(err, "parsing '%s' failed: unable to unquote mount point field", fields[4]) |
91 | | - } |
92 | | - p.Options = fields[5] |
93 | | - |
94 | | - // one or more optional fields, when a separator (-) |
95 | | - i := 6 |
96 | | - for ; i < numFields && fields[i] != "-"; i++ { |
97 | | - switch i { |
98 | | - case 6: |
99 | | - p.Optional = fields[6] |
100 | | - default: |
101 | | - /* NOTE there might be more optional fields before the separator |
102 | | - such as fields[7]...fields[N] (where N < separatorIndex), |
103 | | - although as of Linux kernel 4.15 the only known ones are |
104 | | - mount propagation flags in fields[6]. The correct |
105 | | - behavior is to ignore any unknown optional fields. |
106 | | - */ |
107 | | - } |
108 | | - } |
109 | | - if i == numFields { |
110 | | - return nil, errors.Errorf("parsing '%s' failed: missing separator ('-')", text) |
111 | | - } |
112 | | - // There should be 3 fields after the separator... |
113 | | - if i+4 > numFields { |
114 | | - return nil, errors.Errorf("parsing '%s' failed: not enough fields after a separator", text) |
115 | | - } |
116 | | - // ... but in Linux <= 3.9 mounting a cifs with spaces in a share name |
117 | | - // (like "//serv/My Documents") _may_ end up having a space in the last field |
118 | | - // of mountinfo (like "unc=//serv/My Documents"). Since kernel 3.10-rc1, cifs |
119 | | - // option unc= is ignored, so a space should not appear. In here we ignore |
120 | | - // those "extra" fields caused by extra spaces. |
121 | | - p.FSType = fields[i+1] |
122 | | - p.Source = fields[i+2] |
123 | | - p.VFSOptions = fields[i+3] |
124 | | - |
125 | | - out = append(out, p) |
126 | | - } |
127 | | - if err = s.Err(); err != nil { |
128 | | - return nil, err |
129 | | - } |
130 | | - |
131 | | - return out, nil |
| 27 | + m, err := mountinfo.GetMounts(nil) |
| 28 | + return fromMountinfoSlice(m), err |
132 | 29 | } |
133 | 30 |
|
134 | 31 | // PID collects the mounts for a specific process ID. If the process |
135 | 32 | // ID is unknown, it is better to use `Self` which will inspect |
136 | 33 | // "/proc/self/mountinfo" instead. |
137 | 34 | func PID(pid int) ([]Info, error) { |
138 | | - f, err := os.Open(fmt.Sprintf("/proc/%d/mountinfo", pid)) |
139 | | - if err != nil { |
140 | | - return nil, err |
141 | | - } |
142 | | - defer f.Close() |
143 | | - |
144 | | - return parseInfoFile(f) |
| 35 | + m, err := mountinfo.PidMountInfo(pid) |
| 36 | + return fromMountinfoSlice(m), err |
145 | 37 | } |
0 commit comments