github.phpd.cn/cilium/cilium@v1.6.12/pkg/mountinfo/mountinfo.go (about) 1 // Copyright 2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mountinfo 16 17 import ( 18 "bufio" 19 "fmt" 20 "io" 21 "os" 22 "strconv" 23 "strings" 24 ) 25 26 const ( 27 // FilesystemType names for filesystem which are used in /proc/pid/mountinfo 28 FilesystemTypeBPFFS = "bpf" 29 FilesystemTypeCgroup2 = "cgroup2" 30 31 mountInfoFilepath = "/proc/self/mountinfo" 32 ) 33 34 // MountInfo is a struct representing information from /proc/pid/mountinfo. More 35 // information about file syntax: 36 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt 37 type MountInfo struct { 38 MountID int64 39 ParentID int64 40 StDev string 41 Root string 42 MountPoint string 43 MountOptions string 44 OptionalFields []string 45 FilesystemType string 46 MountSource string 47 SuperOptions string 48 } 49 50 // parseMountInfoFile returns a slice of *MountInfo with information parsed from 51 // the given reader 52 func parseMountInfoFile(r io.Reader) ([]*MountInfo, error) { 53 var result []*MountInfo 54 55 scanner := bufio.NewScanner(r) 56 scanner.Split(bufio.ScanLines) 57 58 for scanner.Scan() { 59 mountInfoRaw := scanner.Text() 60 61 // Optional fields (which are on the 7th position) are separated 62 // from the rest of fields by "-" character. The number of 63 // optional fields can be greater or equal to 1. 64 mountInfoSeparated := strings.Split(mountInfoRaw, " - ") 65 if len(mountInfoSeparated) != 2 { 66 return nil, fmt.Errorf("invalid mountinfo entry which has more that one '-' separator: %s", mountInfoRaw) 67 } 68 69 // Extract fields from both sides of mountinfo 70 mountInfoLeft := strings.Split(strings.TrimSpace(mountInfoSeparated[0]), " ") 71 mountInfoRight := strings.Split(strings.TrimSpace(mountInfoSeparated[1]), " ") 72 73 // Before '-' separator there should be 6 fields and unknown 74 // number of optional fields 75 if len(mountInfoLeft) < 6 { 76 return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw) 77 } 78 // After '-' separator there should be 3 fields 79 if len(mountInfoRight) != 3 { 80 return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw) 81 } 82 83 mountID, err := strconv.ParseInt(mountInfoLeft[0], 10, 64) 84 if err != nil { 85 return nil, err 86 } 87 88 parentID, err := strconv.ParseInt(mountInfoLeft[1], 10, 64) 89 if err != nil { 90 return nil, err 91 } 92 93 // Extract optional fields, which start from 7th position 94 var optionalFields []string 95 for i := 6; i < len(mountInfoLeft); i++ { 96 optionalFields = append(optionalFields, mountInfoLeft[i]) 97 } 98 99 result = append(result, &MountInfo{ 100 MountID: mountID, 101 ParentID: parentID, 102 StDev: mountInfoLeft[2], 103 Root: mountInfoLeft[3], 104 MountPoint: mountInfoLeft[4], 105 MountOptions: mountInfoLeft[5], 106 OptionalFields: optionalFields, 107 FilesystemType: mountInfoRight[0], 108 MountSource: mountInfoRight[1], 109 SuperOptions: mountInfoRight[2], 110 }) 111 } 112 113 return result, nil 114 } 115 116 // GetMountInfo returns a slice of *MountInfo with information parsed from 117 // /proc/self/mountinfo 118 func GetMountInfo() ([]*MountInfo, error) { 119 fMounts, err := os.Open(mountInfoFilepath) 120 if err != nil { 121 return nil, fmt.Errorf("failed to open mount information at %s: %s", mountInfoFilepath, err) 122 } 123 defer fMounts.Close() 124 125 return parseMountInfoFile(fMounts) 126 } 127 128 func isMountFS(mountInfos []*MountInfo, mntType string, mapRoot string) (bool, bool) { 129 var mapRootMountInfo *MountInfo 130 131 for _, mountInfo := range mountInfos { 132 if mountInfo.MountPoint == mapRoot { 133 mapRootMountInfo = mountInfo 134 break 135 } 136 } 137 138 if mapRootMountInfo == nil { 139 return false, false 140 } 141 142 if mapRootMountInfo.FilesystemType == mntType { 143 return true, true 144 } 145 return true, false 146 } 147 148 // IsMountFS returns two boolean values:checks whether the current mapRoot: 149 // - whether the current mapRoot has any mount 150 // - whether that mount's filesystem is of type mntType 151 func IsMountFS(mntType string, mapRoot string) (bool, bool, error) { 152 mountInfos, err := GetMountInfo() 153 if err != nil { 154 return false, false, err 155 } 156 157 mounted, mntTypeInstance := isMountFS(mountInfos, mntType, mapRoot) 158 return mounted, mntTypeInstance, nil 159 }