mirror of
https://github.com/containers/podman.git
synced 2026-06-02 04:49:04 -04:00
Using os.Is{Exist,NotExist,Permission} checks is not recommended in the
new code (see official documentation). While using it in the existing
code is OK, it may still result in a subtle errors later (for a specific
example of that, see [1]).
Replace those with errors.Is.
Generated by:
gofmt -r 'os.IsExist(a) -> errors.Is(a, os.ErrExist)' -w .
gofmt -r 'os.IsNotExist(a) -> errors.Is(a, os.ErrNotExist)' -w .
gofmt -r 'os.IsPermission(a) -> errors.Is(a, os.ErrPermission)' -w .
goimports -w .
git diff vendor test/tools/vendor | patch -p1 -R
[1]: https://github.com/opencontainers/runc/pull/5061
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
214 lines
5.5 KiB
Go
214 lines
5.5 KiB
Go
package rootless
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/moby/sys/user"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"go.podman.io/storage/pkg/fileutils"
|
|
"go.podman.io/storage/pkg/lockfile"
|
|
)
|
|
|
|
// GetNamespaceHandlesPath returns the path to the namespace handles file
|
|
// in the given state directory.
|
|
func GetNamespaceHandlesPath(stateDir string) string {
|
|
return filepath.Join(stateDir, "ns_handles")
|
|
}
|
|
|
|
// GetPausePidPath returns the path to the pause.pid file
|
|
// in the given state directory.
|
|
func GetPausePidPath(stateDir string) string {
|
|
return filepath.Join(stateDir, "pause.pid")
|
|
}
|
|
|
|
// TryJoinPauseProcess attempts to join the namespaces of the pause PID via
|
|
// TryJoinFromFilePaths. If joining fails, it attempts to delete the specified
|
|
// file.
|
|
func TryJoinPauseProcess(stateDir string) (bool, int, error) {
|
|
nsHandlesPath := GetNamespaceHandlesPath(stateDir)
|
|
if err := fileutils.Exists(nsHandlesPath); err == nil {
|
|
return false, -1, nil
|
|
}
|
|
|
|
pausePidPath := GetPausePidPath(stateDir)
|
|
if err := fileutils.Exists(pausePidPath); err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return false, -1, nil
|
|
}
|
|
return false, -1, err
|
|
}
|
|
|
|
became, ret, err := TryJoinFromFilePaths("", []string{pausePidPath})
|
|
if err == nil {
|
|
return became, ret, nil
|
|
}
|
|
|
|
// It could not join the pause process, let's lock the file before trying to delete it.
|
|
pidFileLock, err := lockfile.GetLockFile(pausePidPath)
|
|
if err != nil {
|
|
// The file was deleted by another process.
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return false, -1, nil
|
|
}
|
|
return false, -1, fmt.Errorf("acquiring lock on %s: %w", pausePidPath, err)
|
|
}
|
|
|
|
pidFileLock.Lock()
|
|
defer func() {
|
|
pidFileLock.Unlock()
|
|
}()
|
|
|
|
// Now the pause PID file is locked. Try to join once again in case it changed while it was not locked.
|
|
became, ret, err = TryJoinFromFilePaths("", []string{pausePidPath})
|
|
if err != nil {
|
|
// It is still failing. We can safely remove it.
|
|
os.Remove(pausePidPath)
|
|
return false, -1, nil
|
|
}
|
|
return became, ret, err
|
|
}
|
|
|
|
var (
|
|
uidMap []user.IDMap
|
|
uidMapError error
|
|
uidMapOnce sync.Once
|
|
|
|
gidMap []user.IDMap
|
|
gidMapError error
|
|
gidMapOnce sync.Once
|
|
)
|
|
|
|
// GetAvailableUIDMap returns the UID mappings in the
|
|
// current user namespace.
|
|
func GetAvailableUIDMap() ([]user.IDMap, error) {
|
|
uidMapOnce.Do(func() {
|
|
var err error
|
|
uidMap, err = user.ParseIDMapFile("/proc/self/uid_map")
|
|
if err != nil {
|
|
uidMapError = err
|
|
return
|
|
}
|
|
})
|
|
return uidMap, uidMapError
|
|
}
|
|
|
|
// GetAvailableGIDMap returns the GID mappings in the
|
|
// current user namespace.
|
|
func GetAvailableGIDMap() ([]user.IDMap, error) {
|
|
gidMapOnce.Do(func() {
|
|
var err error
|
|
gidMap, err = user.ParseIDMapFile("/proc/self/gid_map")
|
|
if err != nil {
|
|
gidMapError = err
|
|
return
|
|
}
|
|
})
|
|
return gidMap, gidMapError
|
|
}
|
|
|
|
// GetAvailableIDMaps returns the UID and GID mappings in the
|
|
// current user namespace.
|
|
func GetAvailableIDMaps() ([]user.IDMap, []user.IDMap, error) {
|
|
u, err := GetAvailableUIDMap()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
g, err := GetAvailableGIDMap()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return u, g, nil
|
|
}
|
|
|
|
func countAvailableIDs(mappings []user.IDMap) int64 {
|
|
availableUids := int64(0)
|
|
for _, r := range mappings {
|
|
availableUids += r.Count
|
|
}
|
|
return availableUids
|
|
}
|
|
|
|
// GetAvailableGids returns how many GIDs are available in the
|
|
// current user namespace.
|
|
func GetAvailableGids() (int64, error) {
|
|
gids, err := GetAvailableGIDMap()
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
|
|
return countAvailableIDs(gids), nil
|
|
}
|
|
|
|
// findIDInMappings find the mapping that contains the specified ID.
|
|
// It assumes availableMappings is sorted by ID.
|
|
func findIDInMappings(id int64, availableMappings []user.IDMap) *user.IDMap {
|
|
i := sort.Search(len(availableMappings), func(i int) bool {
|
|
return availableMappings[i].ID <= id
|
|
})
|
|
if i < 0 || i >= len(availableMappings) {
|
|
return nil
|
|
}
|
|
r := &availableMappings[i]
|
|
if id >= r.ID && id < r.ID+r.Count {
|
|
return r
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MaybeSplitMappings checks whether the specified OCI mappings are possible
|
|
// in the current user namespace or the specified ranges must be split.
|
|
func MaybeSplitMappings(mappings []spec.LinuxIDMapping, availableMappings []user.IDMap) []spec.LinuxIDMapping {
|
|
var ret []spec.LinuxIDMapping
|
|
var overflow spec.LinuxIDMapping
|
|
overflow.Size = 0
|
|
consumed := 0
|
|
sort.Slice(availableMappings, func(i, j int) bool {
|
|
return availableMappings[i].ID > availableMappings[j].ID
|
|
})
|
|
for {
|
|
cur := overflow
|
|
// if there is no overflow left from the previous request, get the next one
|
|
if cur.Size == 0 {
|
|
if consumed == len(mappings) {
|
|
// all done
|
|
return ret
|
|
}
|
|
cur = mappings[consumed]
|
|
consumed++
|
|
}
|
|
|
|
// Find the range where the first specified ID is present
|
|
r := findIDInMappings(int64(cur.HostID), availableMappings)
|
|
if r == nil {
|
|
// The requested range is not available. Just return the original request
|
|
// and let other layers deal with it.
|
|
return mappings
|
|
}
|
|
|
|
offsetInRange := cur.HostID - uint32(r.ID)
|
|
|
|
usableIDs := uint32(r.Count) - offsetInRange
|
|
|
|
// the current range can satisfy the whole request
|
|
if usableIDs >= cur.Size {
|
|
// reset the overflow
|
|
overflow.Size = 0
|
|
} else {
|
|
// the current range can satisfy the request partially
|
|
// so move the rest to overflow
|
|
overflow.Size = cur.Size - usableIDs
|
|
overflow.ContainerID = cur.ContainerID + usableIDs
|
|
overflow.HostID = cur.HostID + usableIDs
|
|
|
|
// and cap to the usableIDs count
|
|
cur.Size = usableIDs
|
|
}
|
|
ret = append(ret, cur)
|
|
}
|
|
}
|