mountpoint.go (4867B)
1 // Copyright (c) 2017 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 //go:build linux 22 // +build linux 23 24 package cgroups 25 26 import ( 27 "bufio" 28 "os" 29 "path/filepath" 30 "strconv" 31 "strings" 32 ) 33 34 const ( 35 _mountInfoSep = " " 36 _mountInfoOptsSep = "," 37 _mountInfoOptionalFieldsSep = "-" 38 ) 39 40 const ( 41 _miFieldIDMountID = iota 42 _miFieldIDParentID 43 _miFieldIDDeviceID 44 _miFieldIDRoot 45 _miFieldIDMountPoint 46 _miFieldIDOptions 47 _miFieldIDOptionalFields 48 49 _miFieldCountFirstHalf 50 ) 51 52 const ( 53 _miFieldOffsetFSType = iota 54 _miFieldOffsetMountSource 55 _miFieldOffsetSuperOptions 56 57 _miFieldCountSecondHalf 58 ) 59 60 const _miFieldCountMin = _miFieldCountFirstHalf + _miFieldCountSecondHalf 61 62 // MountPoint is the data structure for the mount points in 63 // `/proc/$PID/mountinfo`. See also proc(5) for more information. 64 type MountPoint struct { 65 MountID int 66 ParentID int 67 DeviceID string 68 Root string 69 MountPoint string 70 Options []string 71 OptionalFields []string 72 FSType string 73 MountSource string 74 SuperOptions []string 75 } 76 77 // NewMountPointFromLine parses a line read from `/proc/$PID/mountinfo` and 78 // returns a new *MountPoint. 79 func NewMountPointFromLine(line string) (*MountPoint, error) { 80 fields := strings.Split(line, _mountInfoSep) 81 82 if len(fields) < _miFieldCountMin { 83 return nil, mountPointFormatInvalidError{line} 84 } 85 86 mountID, err := strconv.Atoi(fields[_miFieldIDMountID]) 87 if err != nil { 88 return nil, err 89 } 90 91 parentID, err := strconv.Atoi(fields[_miFieldIDParentID]) 92 if err != nil { 93 return nil, err 94 } 95 96 for i, field := range fields[_miFieldIDOptionalFields:] { 97 if field == _mountInfoOptionalFieldsSep { 98 fsTypeStart := _miFieldIDOptionalFields + i + 1 99 100 if len(fields) != fsTypeStart+_miFieldCountSecondHalf { 101 return nil, mountPointFormatInvalidError{line} 102 } 103 104 miFieldIDFSType := _miFieldOffsetFSType + fsTypeStart 105 miFieldIDMountSource := _miFieldOffsetMountSource + fsTypeStart 106 miFieldIDSuperOptions := _miFieldOffsetSuperOptions + fsTypeStart 107 108 return &MountPoint{ 109 MountID: mountID, 110 ParentID: parentID, 111 DeviceID: fields[_miFieldIDDeviceID], 112 Root: fields[_miFieldIDRoot], 113 MountPoint: fields[_miFieldIDMountPoint], 114 Options: strings.Split(fields[_miFieldIDOptions], _mountInfoOptsSep), 115 OptionalFields: fields[_miFieldIDOptionalFields:(fsTypeStart - 1)], 116 FSType: fields[miFieldIDFSType], 117 MountSource: fields[miFieldIDMountSource], 118 SuperOptions: strings.Split(fields[miFieldIDSuperOptions], _mountInfoOptsSep), 119 }, nil 120 } 121 } 122 123 return nil, mountPointFormatInvalidError{line} 124 } 125 126 // Translate converts an absolute path inside the *MountPoint's file system to 127 // the host file system path in the mount namespace the *MountPoint belongs to. 128 func (mp *MountPoint) Translate(absPath string) (string, error) { 129 relPath, err := filepath.Rel(mp.Root, absPath) 130 131 if err != nil { 132 return "", err 133 } 134 if relPath == ".." || strings.HasPrefix(relPath, "../") { 135 return "", pathNotExposedFromMountPointError{ 136 mountPoint: mp.MountPoint, 137 root: mp.Root, 138 path: absPath, 139 } 140 } 141 142 return filepath.Join(mp.MountPoint, relPath), nil 143 } 144 145 // parseMountInfo parses procPathMountInfo (usually at `/proc/$PID/mountinfo`) 146 // and yields parsed *MountPoint into newMountPoint. 147 func parseMountInfo(procPathMountInfo string, newMountPoint func(*MountPoint) error) error { 148 mountInfoFile, err := os.Open(procPathMountInfo) 149 if err != nil { 150 return err 151 } 152 defer mountInfoFile.Close() 153 154 scanner := bufio.NewScanner(mountInfoFile) 155 156 for scanner.Scan() { 157 mountPoint, err := NewMountPointFromLine(scanner.Text()) 158 if err != nil { 159 return err 160 } 161 if err := newMountPoint(mountPoint); err != nil { 162 return err 163 } 164 } 165 166 return scanner.Err() 167 }