podman
1package rootless2
3import (4"errors"5"fmt"6"os"7"sort"8"sync"9
10"github.com/containers/storage/pkg/fileutils"11"github.com/containers/storage/pkg/lockfile"12"github.com/moby/sys/user"13spec "github.com/opencontainers/runtime-spec/specs-go"14)
15
16// TryJoinPauseProcess attempts to join the namespaces of the pause PID via
17// TryJoinFromFilePaths. If joining fails, it attempts to delete the specified
18// file.
19func TryJoinPauseProcess(pausePidPath string) (bool, int, error) {20if err := fileutils.Exists(pausePidPath); err != nil {21if errors.Is(err, os.ErrNotExist) {22return false, -1, nil23}24return false, -1, err25}26
27became, ret, err := TryJoinFromFilePaths("", false, []string{pausePidPath})28if err == nil {29return became, ret, nil30}31
32// It could not join the pause process, let's lock the file before trying to delete it.33pidFileLock, err := lockfile.GetLockFile(pausePidPath)34if err != nil {35// The file was deleted by another process.36if os.IsNotExist(err) {37return false, -1, nil38}39return false, -1, fmt.Errorf("acquiring lock on %s: %w", pausePidPath, err)40}41
42pidFileLock.Lock()43defer func() {44pidFileLock.Unlock()45}()46
47// Now the pause PID file is locked. Try to join once again in case it changed while it was not locked.48became, ret, err = TryJoinFromFilePaths("", false, []string{pausePidPath})49if err != nil {50// It is still failing. We can safely remove it.51os.Remove(pausePidPath)52return false, -1, nil //nolint: nilerr53}54return became, ret, err55}
56
57var (58uidMap []user.IDMap59uidMapError error60uidMapOnce sync.Once61
62gidMap []user.IDMap63gidMapError error64gidMapOnce sync.Once65)
66
67// GetAvailableUIDMap returns the UID mappings in the
68// current user namespace.
69func GetAvailableUIDMap() ([]user.IDMap, error) {70uidMapOnce.Do(func() {71var err error72uidMap, err = user.ParseIDMapFile("/proc/self/uid_map")73if err != nil {74uidMapError = err75return76}77})78return uidMap, uidMapError79}
80
81// GetAvailableGIDMap returns the GID mappings in the
82// current user namespace.
83func GetAvailableGIDMap() ([]user.IDMap, error) {84gidMapOnce.Do(func() {85var err error86gidMap, err = user.ParseIDMapFile("/proc/self/gid_map")87if err != nil {88gidMapError = err89return90}91})92return gidMap, gidMapError93}
94
95// GetAvailableIDMaps returns the UID and GID mappings in the
96// current user namespace.
97func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) {98u, err := GetAvailableUIDMap()99if err != nil {100return nil, nil, err101}102g, err := GetAvailableGIDMap()103if err != nil {104return nil, nil, err105}106return u, g, nil107}
108
109func countAvailableIDs(mappings []user.IDMap) int64 {110availableUids := int64(0)111for _, r := range mappings {112availableUids += r.Count113}114return availableUids115}
116
117// GetAvailableUids returns how many UIDs are available in the
118// current user namespace.
119func GetAvailableUids() (int64, error) {120uids, err := GetAvailableUIDMap()121if err != nil {122return -1, err123}124
125return countAvailableIDs(uids), nil126}
127
128// GetAvailableGids returns how many GIDs are available in the
129// current user namespace.
130func GetAvailableGids() (int64, error) {131gids, err := GetAvailableGIDMap()132if err != nil {133return -1, err134}135
136return countAvailableIDs(gids), nil137}
138
139// findIDInMappings find the mapping that contains the specified ID.
140// It assumes availableMappings is sorted by ID.
141func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap {142i := sort.Search(len(availableMappings), func(i int) bool {143return availableMappings[i].ID <= id144})145if i < 0 || i >= len(availableMappings) {146return nil147}148r := &availableMappings[i]149if id >= r.ID && id < r.ID+r.Count {150return r151}152return nil153}
154
155// MaybeSplitMappings checks whether the specified OCI mappings are possible
156// in the current user namespace or the specified ranges must be split.
157func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping {158var ret []spec.LinuxIDMapping159var overflow spec.LinuxIDMapping160overflow.Size = 0161consumed := 0162sort.Slice(availableMappings, func(i, j int) bool {163return availableMappings[i].ID > availableMappings[j].ID164})165for {166cur := overflow167// if there is no overflow left from the previous request, get the next one168if cur.Size == 0 {169if consumed == len(mappings) {170// all done171return ret172}173cur = mappings[consumed]174consumed++175}176
177// Find the range where the first specified ID is present178r := findIDInMappings(int64(cur.HostID), availableMappings)179if r == nil {180// The requested range is not available. Just return the original request181// and let other layers deal with it.182return mappings183}184
185offsetInRange := cur.HostID - uint32(r.ID)186
187usableIDs := uint32(r.Count) - offsetInRange188
189// the current range can satisfy the whole request190if usableIDs >= cur.Size {191// reset the overflow192overflow.Size = 0193} else {194// the current range can satisfy the request partially195// so move the rest to overflow196overflow.Size = cur.Size - usableIDs197overflow.ContainerID = cur.ContainerID + usableIDs198overflow.HostID = cur.HostID + usableIDs199
200// and cap to the usableIDs count201cur.Size = usableIDs202}203ret = append(ret, cur)204}205}
206