mirror of
https://github.com/containers/podman.git
synced 2025-08-15 11:55:55 +08:00
![dependabot-preview[bot]](/assets/img/avatar_default.png)
Bumps [github.com/containers/storage](https://github.com/containers/storage) from 1.16.5 to 1.16.6. - [Release notes](https://github.com/containers/storage/releases) - [Changelog](https://github.com/containers/storage/blob/master/docs/containers-storage-changes.md) - [Commits](https://github.com/containers/storage/compare/v1.16.5...v1.16.6) Signed-off-by: dependabot-preview[bot] <support@dependabot.com> Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
251 lines
7.6 KiB
Go
251 lines
7.6 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"os/user"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/containers/storage/pkg/homedir"
|
|
"github.com/containers/storage/pkg/idtools"
|
|
"github.com/containers/storage/pkg/system"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// ParseIDMapping takes idmappings and subuid and subgid maps and returns a storage mapping
|
|
func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap string) (*IDMappingOptions, error) {
|
|
options := IDMappingOptions{
|
|
HostUIDMapping: true,
|
|
HostGIDMapping: true,
|
|
}
|
|
if subGIDMap == "" && subUIDMap != "" {
|
|
subGIDMap = subUIDMap
|
|
}
|
|
if subUIDMap == "" && subGIDMap != "" {
|
|
subUIDMap = subGIDMap
|
|
}
|
|
if len(GIDMapSlice) == 0 && len(UIDMapSlice) != 0 {
|
|
GIDMapSlice = UIDMapSlice
|
|
}
|
|
if len(UIDMapSlice) == 0 && len(GIDMapSlice) != 0 {
|
|
UIDMapSlice = GIDMapSlice
|
|
}
|
|
if len(UIDMapSlice) == 0 && subUIDMap == "" && os.Getuid() != 0 {
|
|
UIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getuid())}
|
|
}
|
|
if len(GIDMapSlice) == 0 && subGIDMap == "" && os.Getuid() != 0 {
|
|
GIDMapSlice = []string{fmt.Sprintf("0:%d:1", os.Getgid())}
|
|
}
|
|
|
|
if subUIDMap != "" && subGIDMap != "" {
|
|
mappings, err := idtools.NewIDMappings(subUIDMap, subGIDMap)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to create NewIDMappings for uidmap=%s gidmap=%s", subUIDMap, subGIDMap)
|
|
}
|
|
options.UIDMap = mappings.UIDs()
|
|
options.GIDMap = mappings.GIDs()
|
|
}
|
|
parsedUIDMap, err := idtools.ParseIDMap(UIDMapSlice, "UID")
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to create ParseUIDMap UID=%s", UIDMapSlice)
|
|
}
|
|
parsedGIDMap, err := idtools.ParseIDMap(GIDMapSlice, "GID")
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "failed to create ParseGIDMap GID=%s", UIDMapSlice)
|
|
}
|
|
options.UIDMap = append(options.UIDMap, parsedUIDMap...)
|
|
options.GIDMap = append(options.GIDMap, parsedGIDMap...)
|
|
if len(options.UIDMap) > 0 {
|
|
options.HostUIDMapping = false
|
|
}
|
|
if len(options.GIDMap) > 0 {
|
|
options.HostGIDMapping = false
|
|
}
|
|
return &options, nil
|
|
}
|
|
|
|
// GetRootlessRuntimeDir returns the runtime directory when running as non root
|
|
func GetRootlessRuntimeDir(rootlessUID int) (string, error) {
|
|
path, err := getRootlessRuntimeDir(rootlessUID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
path = filepath.Join(path, "containers")
|
|
if err := os.MkdirAll(path, 0700); err != nil {
|
|
return "", errors.Wrapf(err, "unable to make rootless runtime dir %s", path)
|
|
}
|
|
return path, nil
|
|
}
|
|
|
|
func getRootlessRuntimeDir(rootlessUID int) (string, error) {
|
|
runtimeDir, err := homedir.GetRuntimeDir()
|
|
if err == nil {
|
|
return runtimeDir, nil
|
|
}
|
|
tmpDir := fmt.Sprintf("/run/user/%d", rootlessUID)
|
|
st, err := system.Stat(tmpDir)
|
|
if err == nil && int(st.UID()) == os.Getuid() && st.Mode()&0700 == 0700 && st.Mode()&0066 == 0000 {
|
|
return tmpDir, nil
|
|
}
|
|
tmpDir = fmt.Sprintf("%s/%d", os.TempDir(), rootlessUID)
|
|
if err := os.MkdirAll(tmpDir, 0700); err != nil {
|
|
logrus.Errorf("failed to create %s: %v", tmpDir, err)
|
|
} else {
|
|
return tmpDir, nil
|
|
}
|
|
home := homedir.Get()
|
|
if home == "" {
|
|
return "", errors.Wrapf(err, "neither XDG_RUNTIME_DIR nor HOME was set non-empty")
|
|
}
|
|
resolvedHome, err := filepath.EvalSymlinks(home)
|
|
if err != nil {
|
|
return "", errors.Wrapf(err, "cannot resolve %s", home)
|
|
}
|
|
return filepath.Join(resolvedHome, "rundir"), nil
|
|
}
|
|
|
|
// getRootlessDirInfo returns the parent path of where the storage for containers and
|
|
// volumes will be in rootless mode
|
|
func getRootlessDirInfo(rootlessUID int) (string, string, error) {
|
|
rootlessRuntime, err := GetRootlessRuntimeDir(rootlessUID)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
dataDir, err := homedir.GetDataHome()
|
|
if err == nil {
|
|
return dataDir, rootlessRuntime, nil
|
|
}
|
|
|
|
home := homedir.Get()
|
|
if home == "" {
|
|
return "", "", errors.Wrapf(err, "neither XDG_DATA_HOME nor HOME was set non-empty")
|
|
}
|
|
// runc doesn't like symlinks in the rootfs path, and at least
|
|
// on CoreOS /home is a symlink to /var/home, so resolve any symlink.
|
|
resolvedHome, err := filepath.EvalSymlinks(home)
|
|
if err != nil {
|
|
return "", "", errors.Wrapf(err, "cannot resolve %s", home)
|
|
}
|
|
dataDir = filepath.Join(resolvedHome, ".local", "share")
|
|
|
|
return dataDir, rootlessRuntime, nil
|
|
}
|
|
|
|
// getRootlessStorageOpts returns the storage opts for containers running as non root
|
|
func getRootlessStorageOpts(rootlessUID int) (StoreOptions, error) {
|
|
var opts StoreOptions
|
|
|
|
dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUID)
|
|
if err != nil {
|
|
return opts, err
|
|
}
|
|
opts.RunRoot = rootlessRuntime
|
|
opts.GraphRoot = filepath.Join(dataDir, "containers", "storage")
|
|
opts.RootlessStoragePath = opts.GraphRoot
|
|
if path, err := exec.LookPath("fuse-overlayfs"); err == nil {
|
|
opts.GraphDriverName = "overlay"
|
|
opts.GraphDriverOptions = []string{fmt.Sprintf("overlay.mount_program=%s", path)}
|
|
} else {
|
|
opts.GraphDriverName = "vfs"
|
|
}
|
|
return opts, nil
|
|
}
|
|
|
|
func getRootlessUID() int {
|
|
uidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID")
|
|
if uidEnv != "" {
|
|
u, _ := strconv.Atoi(uidEnv)
|
|
return u
|
|
}
|
|
return os.Geteuid()
|
|
}
|
|
|
|
// DefaultStoreOptionsAutoDetectUID returns the default storage ops for containers
|
|
func DefaultStoreOptionsAutoDetectUID() (StoreOptions, error) {
|
|
uid := getRootlessUID()
|
|
return DefaultStoreOptions(uid != 0, uid)
|
|
}
|
|
|
|
// DefaultStoreOptions returns the default storage ops for containers
|
|
func DefaultStoreOptions(rootless bool, rootlessUID int) (StoreOptions, error) {
|
|
var (
|
|
defaultRootlessRunRoot string
|
|
defaultRootlessGraphRoot string
|
|
err error
|
|
)
|
|
storageOpts := defaultStoreOptions
|
|
if rootless && rootlessUID != 0 {
|
|
storageOpts, err = getRootlessStorageOpts(rootlessUID)
|
|
if err != nil {
|
|
return storageOpts, err
|
|
}
|
|
}
|
|
|
|
storageConf, err := DefaultConfigFile(rootless && rootlessUID != 0)
|
|
if err != nil {
|
|
return storageOpts, err
|
|
}
|
|
_, err = os.Stat(storageConf)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
return storageOpts, errors.Wrapf(err, "cannot stat %s", storageConf)
|
|
}
|
|
if err == nil {
|
|
defaultRootlessRunRoot = storageOpts.RunRoot
|
|
defaultRootlessGraphRoot = storageOpts.GraphRoot
|
|
storageOpts = StoreOptions{}
|
|
reloadConfigurationFileIfNeeded(storageConf, &storageOpts)
|
|
}
|
|
|
|
if rootless && rootlessUID != 0 {
|
|
if err == nil {
|
|
// If the file did not specify a graphroot or runroot,
|
|
// set sane defaults so we don't try and use root-owned
|
|
// directories
|
|
if storageOpts.RunRoot == "" {
|
|
storageOpts.RunRoot = defaultRootlessRunRoot
|
|
}
|
|
if storageOpts.GraphRoot == "" {
|
|
storageOpts.GraphRoot = defaultRootlessGraphRoot
|
|
}
|
|
if storageOpts.RootlessStoragePath != "" {
|
|
if err = validRootlessStoragePathFormat(storageOpts.RootlessStoragePath); err != nil {
|
|
return storageOpts, err
|
|
}
|
|
rootlessStoragePath := strings.Replace(storageOpts.RootlessStoragePath, "$HOME", homedir.Get(), -1)
|
|
rootlessStoragePath = strings.Replace(rootlessStoragePath, "$UID", strconv.Itoa(rootlessUID), -1)
|
|
usr, err := user.LookupId(strconv.Itoa(rootlessUID))
|
|
if err != nil {
|
|
return storageOpts, err
|
|
}
|
|
rootlessStoragePath = strings.Replace(rootlessStoragePath, "$USER", usr.Username, -1)
|
|
storageOpts.GraphRoot = rootlessStoragePath
|
|
}
|
|
}
|
|
}
|
|
return storageOpts, nil
|
|
}
|
|
|
|
// validRootlessStoragePathFormat checks if the environments contained in the path are accepted
|
|
func validRootlessStoragePathFormat(path string) error {
|
|
if !strings.Contains(path, "$") {
|
|
return nil
|
|
}
|
|
|
|
splitPaths := strings.SplitAfter(path, "$")
|
|
validEnv := regexp.MustCompile(`^(HOME|USER|UID)([^a-zA-Z]|$)`).MatchString
|
|
if len(splitPaths) > 1 {
|
|
for _, p := range splitPaths[1:] {
|
|
if !validEnv(p) {
|
|
return errors.Errorf("Unrecognized environment variable")
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|