types: use functionalities from pkg/homedir

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
Giuseppe Scrivano 2023-11-08 21:22:31 +01:00
parent 0e6a96bd6f
commit c72a594c83
No known key found for this signature in database
GPG Key ID: 67E38F7A8BA21772
4 changed files with 15 additions and 414 deletions

View File

@ -11,7 +11,9 @@ import (
"github.com/BurntSushi/toml"
cfg "github.com/containers/storage/pkg/config"
"github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/unshare"
"github.com/sirupsen/logrus"
)
@ -273,11 +275,21 @@ func isRootlessDriver(driver string) bool {
func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOptions, error) {
var opts StoreOptions
dataDir, rootlessRuntime, err := getRootlessDirInfo(rootlessUID)
dataDir, err := homedir.GetDataHome()
if err != nil {
return opts, err
}
opts.RunRoot = rootlessRuntime
rootlessRuntime, err := homedir.GetRuntimeDir()
if err != nil {
return opts, err
}
opts.RunRoot = filepath.Join(rootlessRuntime, "containers")
if err := os.MkdirAll(opts.RunRoot, 0o700); err != nil {
return opts, fmt.Errorf("unable to make rootless runtime: %w", err)
}
opts.PullOptions = systemOpts.PullOptions
if systemOpts.RootlessStoragePath != "" {
opts.GraphRoot, err = expandEnvPath(systemOpts.RootlessStoragePath, rootlessUID)
@ -345,7 +357,7 @@ func getRootlessStorageOpts(rootlessUID int, systemOpts StoreOptions) (StoreOpti
// DefaultStoreOptionsAutoDetectUID returns the default storage ops for containers
func DefaultStoreOptionsAutoDetectUID() (StoreOptions, error) {
uid := getRootlessUID()
uid := unshare.GetRootlessUID()
return DefaultStoreOptions(uid != 0, uid)
}

View File

@ -2,162 +2,15 @@ package types
import (
"errors"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/system"
"github.com/sirupsen/logrus"
)
// 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, 0o700); err != nil {
return "", fmt.Errorf("unable to make rootless runtime: %w", err)
}
return path, nil
}
type rootlessRuntimeDirEnvironment interface {
getProcCommandFile() string
getRunUserDir() string
getTmpPerUserDir() string
homeDirGetRuntimeDir() (string, error)
systemLstat(string) (*system.StatT, error)
homedirGet() string
}
type rootlessRuntimeDirEnvironmentImplementation struct {
procCommandFile string
runUserDir string
tmpPerUserDir string
}
func (env rootlessRuntimeDirEnvironmentImplementation) getProcCommandFile() string {
return env.procCommandFile
}
func (env rootlessRuntimeDirEnvironmentImplementation) getRunUserDir() string {
return env.runUserDir
}
func (env rootlessRuntimeDirEnvironmentImplementation) getTmpPerUserDir() string {
return env.tmpPerUserDir
}
func (rootlessRuntimeDirEnvironmentImplementation) homeDirGetRuntimeDir() (string, error) {
return homedir.GetRuntimeDir()
}
func (rootlessRuntimeDirEnvironmentImplementation) systemLstat(path string) (*system.StatT, error) {
return system.Lstat(path)
}
func (rootlessRuntimeDirEnvironmentImplementation) homedirGet() string {
return homedir.Get()
}
func isRootlessRuntimeDirOwner(dir string, env rootlessRuntimeDirEnvironment) bool {
st, err := env.systemLstat(dir)
return err == nil && int(st.UID()) == os.Getuid() && st.Mode()&0o700 == 0o700 && st.Mode()&0o066 == 0o000
}
// getRootlessRuntimeDirIsolated is an internal implementation detail of getRootlessRuntimeDir to allow testing.
// Everyone but the tests this is intended for should only call getRootlessRuntimeDir, never this function.
func getRootlessRuntimeDirIsolated(env rootlessRuntimeDirEnvironment) (string, error) {
runtimeDir, err := env.homeDirGetRuntimeDir()
if err == nil {
return runtimeDir, nil
}
initCommand, err := os.ReadFile(env.getProcCommandFile())
if err != nil || string(initCommand) == "systemd" {
runUserDir := env.getRunUserDir()
if isRootlessRuntimeDirOwner(runUserDir, env) {
return runUserDir, nil
}
}
tmpPerUserDir := env.getTmpPerUserDir()
if tmpPerUserDir != "" {
if _, err := env.systemLstat(tmpPerUserDir); os.IsNotExist(err) {
if err := os.Mkdir(tmpPerUserDir, 0o700); err != nil {
logrus.Errorf("Failed to create temp directory for user: %v", err)
} else {
return tmpPerUserDir, nil
}
} else if isRootlessRuntimeDirOwner(tmpPerUserDir, env) {
return tmpPerUserDir, nil
}
}
homeDir := env.homedirGet()
if homeDir == "" {
return "", errors.New("neither XDG_RUNTIME_DIR nor temp dir nor HOME was set non-empty")
}
resolvedHomeDir, err := filepath.EvalSymlinks(homeDir)
if err != nil {
return "", err
}
return filepath.Join(resolvedHomeDir, "rundir"), nil
}
func getRootlessRuntimeDir(rootlessUID int) (string, error) {
return getRootlessRuntimeDirIsolated(
rootlessRuntimeDirEnvironmentImplementation{
"/proc/1/comm",
fmt.Sprintf("/run/user/%d", rootlessUID),
fmt.Sprintf("%s/containers-user-%d", os.TempDir(), rootlessUID),
},
)
}
// 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 "", "", fmt.Errorf("neither XDG_DATA_HOME nor HOME was set non-empty: %w", err)
}
// 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 "", "", err
}
dataDir = filepath.Join(resolvedHome, ".local", "share")
return dataDir, rootlessRuntime, nil
}
func getRootlessUID() int {
uidEnv := os.Getenv("_CONTAINERS_ROOTLESS_UID")
if uidEnv != "" {
u, _ := strconv.Atoi(uidEnv)
return u
}
return os.Geteuid()
}
func expandEnvPath(path string, rootlessUID int) (string, error) {
var err error
path = strings.Replace(path, "$UID", strconv.Itoa(rootlessUID), -1)

View File

@ -1,272 +1,13 @@
package types
import (
"errors"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/containers/storage/pkg/homedir"
"github.com/containers/storage/pkg/system"
"gotest.tools/assert"
)
type homeRuntimeData struct {
dir string
err error
}
type rootlessRuntimeDirEnvironmentTest struct {
homeRuntime homeRuntimeData
procCommandFile string
runUserDir string
tmpPerUserDir string
homeDir string
result string
}
func (env rootlessRuntimeDirEnvironmentTest) getProcCommandFile() string {
return env.procCommandFile
}
func (env rootlessRuntimeDirEnvironmentTest) getRunUserDir() string {
return env.runUserDir
}
func (env rootlessRuntimeDirEnvironmentTest) getTmpPerUserDir() string {
return env.tmpPerUserDir
}
func (env rootlessRuntimeDirEnvironmentTest) homeDirGetRuntimeDir() (string, error) {
return env.homeRuntime.dir, env.homeRuntime.err
}
func (env rootlessRuntimeDirEnvironmentTest) systemLstat(path string) (*system.StatT, error) {
return system.Lstat(path)
}
func (env rootlessRuntimeDirEnvironmentTest) homedirGet() string {
return env.homeDir
}
func TestRootlessRuntimeDir(t *testing.T) {
testDir := t.TempDir()
homeRuntimeDir := filepath.Join(testDir, "home-rundir")
err := os.Mkdir(homeRuntimeDir, 0o700)
assert.NilError(t, err)
homeRuntimeDisabled := homeRuntimeData{err: errors.New("homedirGetRuntimeDir is disabled")}
systemdCommandFile := filepath.Join(testDir, "systemd-command")
err = os.WriteFile(systemdCommandFile, []byte("systemd"), 0o644)
assert.NilError(t, err)
initCommandFile := filepath.Join(testDir, "init-command")
err = os.WriteFile(initCommandFile, []byte("init"), 0o644)
assert.NilError(t, err)
dirForOwner := filepath.Join(testDir, "dir-for-owner")
err = os.Mkdir(dirForOwner, 0o700)
assert.NilError(t, err)
dirForAll := filepath.Join(testDir, "dir-for-all")
err = os.Mkdir(dirForAll, 0o777)
assert.NilError(t, err)
dirToBeCreated := filepath.Join(testDir, "dir-to-be-created")
envs := []rootlessRuntimeDirEnvironmentTest{
{
homeRuntime: homeRuntimeData{dir: homeRuntimeDir},
result: homeRuntimeDir,
},
// Reading proc command file fails
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: "",
runUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: "",
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: "",
runUserDir: dirForAll,
tmpPerUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: "",
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: dirToBeCreated,
result: dirToBeCreated,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: "",
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: "", // Accessing tmp per user dir fails
homeDir: dirForOwner,
result: filepath.Join(dirForOwner, "rundir"),
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: "",
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: dirForAll,
homeDir: dirForOwner,
result: filepath.Join(dirForOwner, "rundir"),
},
// systemd
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: systemdCommandFile,
runUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: systemdCommandFile,
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: systemdCommandFile,
runUserDir: dirForAll,
tmpPerUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: systemdCommandFile,
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: dirToBeCreated,
result: dirToBeCreated,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: systemdCommandFile,
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: "", // Accessing tmp per user dir fails
homeDir: dirForOwner,
result: filepath.Join(dirForOwner, "rundir"),
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: systemdCommandFile,
runUserDir: "", // Accessing run user dir fails
tmpPerUserDir: dirForAll,
homeDir: dirForOwner,
result: filepath.Join(dirForOwner, "rundir"),
},
// init
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: initCommandFile,
tmpPerUserDir: dirForOwner,
result: dirForOwner,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: initCommandFile,
tmpPerUserDir: dirToBeCreated,
result: dirToBeCreated,
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: initCommandFile,
tmpPerUserDir: "", // Accessing tmp per user dir fails
homeDir: dirForOwner,
result: filepath.Join(dirForOwner, "rundir"),
},
{
homeRuntime: homeRuntimeDisabled,
procCommandFile: initCommandFile,
tmpPerUserDir: dirForAll,
homeDir: dirForOwner,
result: filepath.Join(dirForOwner, "rundir"),
},
}
for i, env := range envs {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
os.Remove(dirToBeCreated)
resultDir, err := getRootlessRuntimeDirIsolated(env)
assert.NilError(t, err)
assert.Assert(t, resultDir == env.result)
})
}
}
type rootlessRuntimeDirEnvironmentRace struct {
procCommandFile string
tmpPerUserDir string
}
func (env rootlessRuntimeDirEnvironmentRace) getProcCommandFile() string {
return env.procCommandFile
}
func (rootlessRuntimeDirEnvironmentRace) getRunUserDir() string {
return ""
}
func (env rootlessRuntimeDirEnvironmentRace) getTmpPerUserDir() string {
return env.tmpPerUserDir
}
func (rootlessRuntimeDirEnvironmentRace) homeDirGetRuntimeDir() (string, error) {
return "", errors.New("homedirGetRuntimeDir is disabled")
}
func (env rootlessRuntimeDirEnvironmentRace) systemLstat(path string) (*system.StatT, error) {
if path == env.tmpPerUserDir {
st, err := system.Lstat(path)
// We can simulate that race directory was created immediately after system.Lstat call.
if err := os.Mkdir(path, 0o700); err != nil {
return nil, err
}
return st, err
}
return system.Lstat(path)
}
func (rootlessRuntimeDirEnvironmentRace) homedirGet() string {
return homedir.Get()
}
func TestRootlessRuntimeDirRace(t *testing.T) {
raceDir := t.TempDir()
procCommandFile := filepath.Join(raceDir, "command")
err := os.WriteFile(procCommandFile, []byte("init"), 0o644)
assert.NilError(t, err)
tmpPerUserDir := filepath.Join(raceDir, "tmp")
resultDir, err := getRootlessRuntimeDirIsolated(rootlessRuntimeDirEnvironmentRace{
procCommandFile,
tmpPerUserDir,
})
assert.NilError(t, err)
assert.Assert(t, resultDir != tmpPerUserDir, "Rootless runtime dir shouldn't follow race dir.")
}
func TestDefaultStoreOpts(t *testing.T) {
storageOpts, err := defaultStoreOptionsIsolated(true, 1000, "./storage_test.conf")

View File

@ -11,11 +11,6 @@ func ParseIDMapping(UIDMapSlice, GIDMapSlice []string, subUIDMap, subGIDMap stri
return types.ParseIDMapping(UIDMapSlice, GIDMapSlice, subUIDMap, subGIDMap)
}
// GetRootlessRuntimeDir returns the runtime directory when running as non root
func GetRootlessRuntimeDir(rootlessUID int) (string, error) {
return types.GetRootlessRuntimeDir(rootlessUID)
}
// DefaultStoreOptionsAutoDetectUID returns the default storage options for containers
func DefaultStoreOptionsAutoDetectUID() (types.StoreOptions, error) {
return types.DefaultStoreOptionsAutoDetectUID()