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  }