github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/rootless/rootless.go (about) 1 package rootless 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "sort" 8 "sync" 9 10 "github.com/containers/storage/pkg/lockfile" 11 "github.com/opencontainers/runc/libcontainer/user" 12 spec "github.com/opencontainers/runtime-spec/specs-go" 13 ) 14 15 // TryJoinPauseProcess attempts to join the namespaces of the pause PID via 16 // TryJoinFromFilePaths. If joining fails, it attempts to delete the specified 17 // file. 18 func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { 19 if _, err := os.Stat(pausePidPath); err != nil { 20 if errors.Is(err, os.ErrNotExist) { 21 return false, -1, nil 22 } 23 return false, -1, err 24 } 25 26 became, ret, err := TryJoinFromFilePaths("", false, []string{pausePidPath}) 27 if err == nil { 28 return became, ret, nil 29 } 30 31 // It could not join the pause process, let's lock the file before trying to delete it. 32 pidFileLock, err := lockfile.GetLockfile(pausePidPath) 33 if err != nil { 34 // The file was deleted by another process. 35 if os.IsNotExist(err) { 36 return false, -1, nil 37 } 38 return false, -1, fmt.Errorf("acquiring lock on %s: %w", pausePidPath, err) 39 } 40 41 pidFileLock.Lock() 42 defer func() { 43 if pidFileLock.Locked() { 44 pidFileLock.Unlock() 45 } 46 }() 47 48 // Now the pause PID file is locked. Try to join once again in case it changed while it was not locked. 49 became, ret, err = TryJoinFromFilePaths("", false, []string{pausePidPath}) 50 if err != nil { 51 // It is still failing. We can safely remove it. 52 os.Remove(pausePidPath) 53 return false, -1, nil // nolint: nilerr 54 } 55 return became, ret, err 56 } 57 58 var ( 59 uidMap []user.IDMap 60 uidMapError error 61 uidMapOnce sync.Once 62 63 gidMap []user.IDMap 64 gidMapError error 65 gidMapOnce sync.Once 66 ) 67 68 // GetAvailableUIDMap returns the UID mappings in the 69 // current user namespace. 70 func GetAvailableUIDMap() ([]user.IDMap, error) { 71 uidMapOnce.Do(func() { 72 var err error 73 uidMap, err = user.ParseIDMapFile("/proc/self/uid_map") 74 if err != nil { 75 uidMapError = err 76 return 77 } 78 }) 79 return uidMap, uidMapError 80 } 81 82 // GetAvailableGIDMap returns the GID mappings in the 83 // current user namespace. 84 func GetAvailableGIDMap() ([]user.IDMap, error) { 85 gidMapOnce.Do(func() { 86 var err error 87 gidMap, err = user.ParseIDMapFile("/proc/self/gid_map") 88 if err != nil { 89 gidMapError = err 90 return 91 } 92 }) 93 return gidMap, gidMapError 94 } 95 96 // GetAvailableIDMaps returns the UID and GID mappings in the 97 // current user namespace. 98 func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) { 99 u, err := GetAvailableUIDMap() 100 if err != nil { 101 return nil, nil, err 102 } 103 g, err := GetAvailableGIDMap() 104 if err != nil { 105 return nil, nil, err 106 } 107 return u, g, nil 108 } 109 110 func countAvailableIDs(mappings []user.IDMap) int64 { 111 availableUids := int64(0) 112 for _, r := range mappings { 113 availableUids += r.Count 114 } 115 return availableUids 116 } 117 118 // GetAvailableUids returns how many UIDs are available in the 119 // current user namespace. 120 func GetAvailableUids() (int64, error) { 121 uids, err := GetAvailableUIDMap() 122 if err != nil { 123 return -1, err 124 } 125 126 return countAvailableIDs(uids), nil 127 } 128 129 // GetAvailableGids returns how many GIDs are available in the 130 // current user namespace. 131 func GetAvailableGids() (int64, error) { 132 gids, err := GetAvailableGIDMap() 133 if err != nil { 134 return -1, err 135 } 136 137 return countAvailableIDs(gids), nil 138 } 139 140 // findIDInMappings find the the mapping that contains the specified ID. 141 // It assumes availableMappings is sorted by ID. 142 func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap { 143 i := sort.Search(len(availableMappings), func(i int) bool { 144 return availableMappings[i].ID <= id 145 }) 146 if i < 0 || i >= len(availableMappings) { 147 return nil 148 } 149 r := &availableMappings[i] 150 if id >= r.ID && id < r.ID+r.Count { 151 return r 152 } 153 return nil 154 } 155 156 // MaybeSplitMappings checks whether the specified OCI mappings are possible 157 // in the current user namespace or the specified ranges must be split. 158 func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping { 159 var ret []spec.LinuxIDMapping 160 var overflow spec.LinuxIDMapping 161 overflow.Size = 0 162 consumed := 0 163 sort.Slice(availableMappings, func(i, j int) bool { 164 return availableMappings[i].ID > availableMappings[j].ID 165 }) 166 for { 167 cur := overflow 168 // if there is no overflow left from the previous request, get the next one 169 if cur.Size == 0 { 170 if consumed == len(mappings) { 171 // all done 172 return ret 173 } 174 cur = mappings[consumed] 175 consumed++ 176 } 177 178 // Find the range where the first specified ID is present 179 r := findIDInMappings(int64(cur.HostID), availableMappings) 180 if r == nil { 181 // The requested range is not available. Just return the original request 182 // and let other layers deal with it. 183 return mappings 184 } 185 186 offsetInRange := cur.HostID - uint32(r.ID) 187 188 usableIDs := uint32(r.Count) - offsetInRange 189 190 // the current range can satisfy the whole request 191 if usableIDs >= cur.Size { 192 // reset the overflow 193 overflow.Size = 0 194 } else { 195 // the current range can satisfy the request partially 196 // so move the rest to overflow 197 overflow.Size = cur.Size - usableIDs 198 overflow.ContainerID = cur.ContainerID + usableIDs 199 overflow.HostID = cur.HostID + usableIDs 200 201 // and cap to the usableIDs count 202 cur.Size = usableIDs 203 } 204 ret = append(ret, cur) 205 } 206 }