github.com/containers/podman/v4@v4.9.4/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 pidFileLock.Unlock() 44 }() 45 46 // Now the pause PID file is locked. Try to join once again in case it changed while it was not locked. 47 became, ret, err = TryJoinFromFilePaths("", false, []string{pausePidPath}) 48 if err != nil { 49 // It is still failing. We can safely remove it. 50 os.Remove(pausePidPath) 51 return false, -1, nil //nolint: nilerr 52 } 53 return became, ret, err 54 } 55 56 var ( 57 uidMap []user.IDMap 58 uidMapError error 59 uidMapOnce sync.Once 60 61 gidMap []user.IDMap 62 gidMapError error 63 gidMapOnce sync.Once 64 ) 65 66 // GetAvailableUIDMap returns the UID mappings in the 67 // current user namespace. 68 func GetAvailableUIDMap() ([]user.IDMap, error) { 69 uidMapOnce.Do(func() { 70 var err error 71 uidMap, err = user.ParseIDMapFile("/proc/self/uid_map") 72 if err != nil { 73 uidMapError = err 74 return 75 } 76 }) 77 return uidMap, uidMapError 78 } 79 80 // GetAvailableGIDMap returns the GID mappings in the 81 // current user namespace. 82 func GetAvailableGIDMap() ([]user.IDMap, error) { 83 gidMapOnce.Do(func() { 84 var err error 85 gidMap, err = user.ParseIDMapFile("/proc/self/gid_map") 86 if err != nil { 87 gidMapError = err 88 return 89 } 90 }) 91 return gidMap, gidMapError 92 } 93 94 // GetAvailableIDMaps returns the UID and GID mappings in the 95 // current user namespace. 96 func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) { 97 u, err := GetAvailableUIDMap() 98 if err != nil { 99 return nil, nil, err 100 } 101 g, err := GetAvailableGIDMap() 102 if err != nil { 103 return nil, nil, err 104 } 105 return u, g, nil 106 } 107 108 func countAvailableIDs(mappings []user.IDMap) int64 { 109 availableUids := int64(0) 110 for _, r := range mappings { 111 availableUids += r.Count 112 } 113 return availableUids 114 } 115 116 // GetAvailableUids returns how many UIDs are available in the 117 // current user namespace. 118 func GetAvailableUids() (int64, error) { 119 uids, err := GetAvailableUIDMap() 120 if err != nil { 121 return -1, err 122 } 123 124 return countAvailableIDs(uids), nil 125 } 126 127 // GetAvailableGids returns how many GIDs are available in the 128 // current user namespace. 129 func GetAvailableGids() (int64, error) { 130 gids, err := GetAvailableGIDMap() 131 if err != nil { 132 return -1, err 133 } 134 135 return countAvailableIDs(gids), nil 136 } 137 138 // findIDInMappings find the mapping that contains the specified ID. 139 // It assumes availableMappings is sorted by ID. 140 func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap { 141 i := sort.Search(len(availableMappings), func(i int) bool { 142 return availableMappings[i].ID <= id 143 }) 144 if i < 0 || i >= len(availableMappings) { 145 return nil 146 } 147 r := &availableMappings[i] 148 if id >= r.ID && id < r.ID+r.Count { 149 return r 150 } 151 return nil 152 } 153 154 // MaybeSplitMappings checks whether the specified OCI mappings are possible 155 // in the current user namespace or the specified ranges must be split. 156 func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping { 157 var ret []spec.LinuxIDMapping 158 var overflow spec.LinuxIDMapping 159 overflow.Size = 0 160 consumed := 0 161 sort.Slice(availableMappings, func(i, j int) bool { 162 return availableMappings[i].ID > availableMappings[j].ID 163 }) 164 for { 165 cur := overflow 166 // if there is no overflow left from the previous request, get the next one 167 if cur.Size == 0 { 168 if consumed == len(mappings) { 169 // all done 170 return ret 171 } 172 cur = mappings[consumed] 173 consumed++ 174 } 175 176 // Find the range where the first specified ID is present 177 r := findIDInMappings(int64(cur.HostID), availableMappings) 178 if r == nil { 179 // The requested range is not available. Just return the original request 180 // and let other layers deal with it. 181 return mappings 182 } 183 184 offsetInRange := cur.HostID - uint32(r.ID) 185 186 usableIDs := uint32(r.Count) - offsetInRange 187 188 // the current range can satisfy the whole request 189 if usableIDs >= cur.Size { 190 // reset the overflow 191 overflow.Size = 0 192 } else { 193 // the current range can satisfy the request partially 194 // so move the rest to overflow 195 overflow.Size = cur.Size - usableIDs 196 overflow.ContainerID = cur.ContainerID + usableIDs 197 overflow.HostID = cur.HostID + usableIDs 198 199 // and cap to the usableIDs count 200 cur.Size = usableIDs 201 } 202 ret = append(ret, cur) 203 } 204 }