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  }