@@ -5,15 +5,19 @@ import (
5
5
"fmt"
6
6
"io"
7
7
"os"
8
+ "runtime"
8
9
"strconv"
9
10
"strings"
11
+ "sync"
12
+
13
+ "golang.org/x/sys/unix"
10
14
)
11
15
12
16
// GetMountsFromReader retrieves a list of mounts from the
13
17
// reader provided, with an optional filter applied (use nil
14
18
// for no filter). This can be useful in tests or benchmarks
15
19
// that provide fake mountinfo data, or when a source other
16
- // than /proc/self/mountinfo needs to be read from.
20
+ // than /proc/thread- self/mountinfo needs to be read from.
17
21
//
18
22
// This function is Linux-specific.
19
23
func GetMountsFromReader (r io.Reader , filter FilterFunc ) ([]* Info , error ) {
@@ -127,8 +131,32 @@ func GetMountsFromReader(r io.Reader, filter FilterFunc) ([]*Info, error) {
127
131
return out , nil
128
132
}
129
133
130
- func parseMountTable (filter FilterFunc ) ([]* Info , error ) {
131
- f , err := os .Open ("/proc/self/mountinfo" )
134
+ var (
135
+ haveProcThreadSelf bool
136
+ haveProcThreadSelfOnce sync.Once
137
+ )
138
+
139
+ func parseMountTable (filter FilterFunc ) (_ []* Info , err error ) {
140
+ haveProcThreadSelfOnce .Do (func () {
141
+ _ , err := os .Stat ("/proc/thread-self/mountinfo" )
142
+ haveProcThreadSelf = err == nil
143
+ })
144
+
145
+ // We need to lock ourselves to the current OS thread in order to make sure
146
+ // that the thread referenced by /proc/thread-self stays alive until we
147
+ // finish parsing the file.
148
+ runtime .LockOSThread ()
149
+ defer runtime .UnlockOSThread ()
150
+
151
+ var f * os.File
152
+ if haveProcThreadSelf {
153
+ f , err = os .Open ("/proc/thread-self/mountinfo" )
154
+ } else {
155
+ // On pre-3.17 kernels (such as CentOS 7), we don't have
156
+ // /proc/thread-self/ so we need to manually construct
157
+ // /proc/self/task/<tid>/ as a fallback.
158
+ f , err = os .Open ("/proc/self/task/" + strconv .Itoa (unix .Gettid ()) + "/mountinfo" )
159
+ }
132
160
if err != nil {
133
161
return nil , err
134
162
}
0 commit comments