types: use functionalities from pkg/homedir
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
This commit is contained in:
parent
0e6a96bd6f
commit
c72a594c83
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
147
types/utils.go
147
types/utils.go
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
5
utils.go
5
utils.go
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue