github.com/cilium/cilium@v1.16.2/pkg/mountinfo/mountinfo.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package mountinfo 5 6 import ( 7 "bufio" 8 "fmt" 9 "io" 10 "os" 11 "strconv" 12 "strings" 13 ) 14 15 const ( 16 mountInfoFilepath = "/proc/self/mountinfo" 17 ) 18 19 // MountInfo is a struct representing information from /proc/pid/mountinfo. More 20 // information about file syntax: 21 // https://www.kernel.org/doc/Documentation/filesystems/proc.txt 22 type MountInfo struct { 23 MountID int64 24 ParentID int64 25 StDev string 26 Root string 27 MountPoint string 28 MountOptions string 29 OptionalFields []string 30 FilesystemType string 31 MountSource string 32 SuperOptions string 33 } 34 35 // parseMountInfoFile returns a slice of *MountInfo with information parsed from 36 // the given reader 37 func parseMountInfoFile(r io.Reader) ([]*MountInfo, error) { 38 var result []*MountInfo 39 40 scanner := bufio.NewScanner(r) 41 scanner.Split(bufio.ScanLines) 42 43 for scanner.Scan() { 44 mountInfoRaw := scanner.Text() 45 46 // Optional fields (which are on the 7th position) are separated 47 // from the rest of fields by "-" character. The number of 48 // optional fields can be greater or equal to 1. 49 mountInfoSeparated := strings.Split(mountInfoRaw, " - ") 50 if len(mountInfoSeparated) != 2 { 51 return nil, fmt.Errorf("invalid mountinfo entry which has more that one '-' separator: %s", mountInfoRaw) 52 } 53 54 // Extract fields from both sides of mountinfo 55 mountInfoLeft := strings.Split(mountInfoSeparated[0], " ") 56 mountInfoRight := strings.Split(mountInfoSeparated[1], " ") 57 58 // Before '-' separator there should be 6 fields and unknown 59 // number of optional fields 60 if len(mountInfoLeft) < 6 { 61 return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw) 62 } 63 // After '-' separator there should be 3 fields 64 if len(mountInfoRight) != 3 { 65 return nil, fmt.Errorf("invalid mountinfo entry: %s", mountInfoRaw) 66 } 67 68 mountID, err := strconv.ParseInt(mountInfoLeft[0], 10, 64) 69 if err != nil { 70 return nil, err 71 } 72 73 parentID, err := strconv.ParseInt(mountInfoLeft[1], 10, 64) 74 if err != nil { 75 return nil, err 76 } 77 78 // Extract optional fields, which start from 7th position 79 var optionalFields []string 80 for i := 6; i < len(mountInfoLeft); i++ { 81 optionalFields = append(optionalFields, mountInfoLeft[i]) 82 } 83 84 result = append(result, &MountInfo{ 85 MountID: mountID, 86 ParentID: parentID, 87 StDev: mountInfoLeft[2], 88 Root: mountInfoLeft[3], 89 MountPoint: mountInfoLeft[4], 90 MountOptions: mountInfoLeft[5], 91 OptionalFields: optionalFields, 92 FilesystemType: mountInfoRight[0], 93 MountSource: mountInfoRight[1], 94 SuperOptions: mountInfoRight[2], 95 }) 96 } 97 98 if err := scanner.Err(); err != nil { 99 return nil, err 100 } 101 102 return result, nil 103 } 104 105 // GetMountInfo returns a slice of *MountInfo with information parsed from 106 // /proc/self/mountinfo 107 func GetMountInfo() ([]*MountInfo, error) { 108 fMounts, err := os.Open(mountInfoFilepath) 109 if err != nil { 110 return nil, fmt.Errorf("failed to open mount information at %s: %w", mountInfoFilepath, err) 111 } 112 defer fMounts.Close() 113 114 return parseMountInfoFile(fMounts) 115 }