-
Notifications
You must be signed in to change notification settings - Fork 2
/
mountinfo.go
138 lines (118 loc) · 3.24 KB
/
mountinfo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
// Copyright (C) Felix Geyer <debfx@fobos.de>
package main
import (
"bufio"
"fmt"
"os"
"regexp"
"strconv"
"strings"
"golang.org/x/sys/unix"
)
var regexpOctalEscape = regexp.MustCompile(`\\(\d{1,3})`)
var optionFlagMap map[string]int = map[string]int{
"ro": unix.MS_RDONLY,
"noexec": unix.MS_NOEXEC,
"nosuid": unix.MS_NOSUID,
"nodev": unix.MS_NODEV,
"sync": unix.MS_SYNCHRONOUS,
"dirsync": unix.MS_DIRSYNC,
"silent": unix.MS_SILENT,
"mand": unix.MS_MANDLOCK,
"noatime": unix.MS_NOATIME,
"iversion": unix.MS_I_VERSION,
"nodiratime": unix.MS_NODIRATIME,
"relatime": unix.MS_RELATIME,
"strictatime": unix.MS_STRICTATIME,
}
type mountInfoEntry struct {
mountId string
parentId string
major uint32
minor uint32
root string
mountPoint string
mountOptions string
optionalFields []string
fsType string
mountSource string
superOptions string
}
func (mie *mountInfoEntry) mountFlags() int {
flags := 0
for _, option := range strings.Split(mie.mountOptions, ",") {
flag, ok := optionFlagMap[option]
if ok {
flags = flags | flag
}
}
return flags
}
func octalToChar(match string) string {
codepoint, _ := strconv.ParseInt(match, 8, 8)
return string(rune(codepoint))
}
func unescapeField(field string) string {
return regexpOctalEscape.ReplaceAllStringFunc(field, octalToChar)
}
func parseMountInfo(pathPrefix string, mountInfoPath string) ([]mountInfoEntry, error) {
f, err := os.OpenFile(mountInfoPath, os.O_RDONLY, 0)
if err != nil {
return []mountInfoEntry{}, err
}
defer f.Close()
sc := bufio.NewScanner(f)
result := []mountInfoEntry{}
for sc.Scan() {
line := sc.Text()
fields := strings.Split(line, " ")
for i := range fields {
fields[i] = unescapeField(fields[i])
}
indexDash := -1
for i := 6; i < len(fields); i++ {
if fields[i] == "-" {
indexDash = i
}
}
if indexDash == -1 {
return []mountInfoEntry{}, fmt.Errorf("missing optional fields separator")
}
if len(fields)-indexDash-1 < 3 {
return []mountInfoEntry{}, fmt.Errorf("not enough fields after optional")
}
deviceParts := strings.Split(fields[2], ":")
if len(deviceParts) != 2 {
return []mountInfoEntry{}, fmt.Errorf("invalid major:minor field")
}
major, err := strconv.ParseUint(deviceParts[0], 10, 32)
if err != nil {
return []mountInfoEntry{}, fmt.Errorf("invalid major value")
}
minor, err := strconv.ParseUint(deviceParts[1], 10, 32)
if err != nil {
return []mountInfoEntry{}, fmt.Errorf("invalid minor value")
}
entry := mountInfoEntry{
mountId: fields[0],
parentId: fields[1],
major: uint32(major),
minor: uint32(minor),
root: fields[3],
mountPoint: fields[4],
mountOptions: fields[5],
optionalFields: fields[6:indexDash],
fsType: fields[indexDash+1],
mountSource: fields[indexDash+2],
superOptions: fields[indexDash+3],
}
if entry.mountPoint == pathPrefix || strings.HasPrefix(entry.mountPoint, pathPrefix+"/") {
result = append(result, entry)
}
}
if err := sc.Err(); err != nil {
return []mountInfoEntry{}, err
}
return result, nil
}