Skip to content

Commit

Permalink
Merge pull request #20888 from openshift-cherrypick-robot/cherry-pick…
Browse files Browse the repository at this point in the history
…-20874-to-v4.8

[v4.8] Handle symlinks when checking DB vs runtime configs
  • Loading branch information
openshift-merge-bot[bot] authored Dec 4, 2023
2 parents 4635f40 + 2d20d2e commit 435258c
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 21 deletions.
41 changes: 37 additions & 4 deletions libpod/boltdb_state_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package libpod

import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -94,6 +96,7 @@ type dbConfigValidation struct {
runtimeValue string
key []byte
defaultValue string
isPath bool
}

// Check if the configuration of the database is compatible with the
Expand All @@ -112,42 +115,49 @@ func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error {
runtime.GOOS,
osKey,
runtime.GOOS,
false,
},
{
"libpod root directory (staticdir)",
filepath.Clean(rt.config.Engine.StaticDir),
staticDirKey,
"",
true,
},
{
"libpod temporary files directory (tmpdir)",
filepath.Clean(rt.config.Engine.TmpDir),
tmpDirKey,
"",
true,
},
{
"storage temporary directory (runroot)",
filepath.Clean(rt.StorageConfig().RunRoot),
runRootKey,
storeOpts.RunRoot,
true,
},
{
"storage graph root directory (graphroot)",
filepath.Clean(rt.StorageConfig().GraphRoot),
graphRootKey,
storeOpts.GraphRoot,
true,
},
{
"storage graph driver",
rt.StorageConfig().GraphDriverName,
graphDriverKey,
storeOpts.GraphDriverName,
false,
},
{
"volume path",
rt.config.Engine.VolumePath,
volPathKey,
"",
true,
},
}

Expand Down Expand Up @@ -222,22 +232,45 @@ func readOnlyValidateConfig(bucket *bolt.Bucket, toCheck dbConfigValidation) (bo
}

dbValue := string(keyBytes)
ourValue := toCheck.runtimeValue

// Tolerate symlinks when possible - most relevant for OStree systems
// and rootless containers, where we want to put containers in /home,
// which is symlinked to /var/home.
if toCheck.isPath {
if dbValue != "" {
// Ignore ENOENT on both, on a fresh system some paths
// may not exist this early in Libpod init.
dbVal, err := filepath.EvalSymlinks(dbValue)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return false, fmt.Errorf("evaluating symlinks on DB %s path %q: %w", toCheck.name, dbValue, err)
}
dbValue = dbVal
}
if ourValue != "" {
ourVal, err := filepath.EvalSymlinks(ourValue)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return false, fmt.Errorf("evaluating symlinks on configured %s path %q: %w", toCheck.name, ourValue, err)
}
ourValue = ourVal
}
}

if toCheck.runtimeValue != dbValue {
if ourValue != dbValue {
// If the runtime value is the empty string and default is not,
// check against default.
if toCheck.runtimeValue == "" && toCheck.defaultValue != "" && dbValue == toCheck.defaultValue {
if ourValue == "" && toCheck.defaultValue != "" && dbValue == toCheck.defaultValue {
return true, nil
}

// If the DB value is the empty string, check that the runtime
// value is the default.
if dbValue == "" && toCheck.defaultValue != "" && toCheck.runtimeValue == toCheck.defaultValue {
if dbValue == "" && toCheck.defaultValue != "" && ourValue == toCheck.defaultValue {
return true, nil
}

return true, fmt.Errorf("database %s %q does not match our %s %q: %w",
toCheck.name, dbValue, toCheck.name, toCheck.runtimeValue, define.ErrDBBadConfig)
toCheck.name, dbValue, toCheck.name, ourValue, define.ErrDBBadConfig)
}

return true, nil
Expand Down
54 changes: 37 additions & 17 deletions libpod/sqlite_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"database/sql"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
goruntime "runtime"
Expand Down Expand Up @@ -316,14 +317,14 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
);`

var (
os, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
runtimeOS = goruntime.GOOS
runtimeStaticDir = filepath.Clean(s.runtime.config.Engine.StaticDir)
runtimeTmpDir = filepath.Clean(s.runtime.config.Engine.TmpDir)
runtimeGraphRoot = filepath.Clean(s.runtime.StorageConfig().GraphRoot)
runtimeRunRoot = filepath.Clean(s.runtime.StorageConfig().RunRoot)
runtimeGraphDriver = s.runtime.StorageConfig().GraphDriverName
runtimeVolumePath = filepath.Clean(s.runtime.config.Engine.VolumePath)
dbOS, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
runtimeOS = goruntime.GOOS
runtimeStaticDir = filepath.Clean(s.runtime.config.Engine.StaticDir)
runtimeTmpDir = filepath.Clean(s.runtime.config.Engine.TmpDir)
runtimeGraphRoot = filepath.Clean(s.runtime.StorageConfig().GraphRoot)
runtimeRunRoot = filepath.Clean(s.runtime.StorageConfig().RunRoot)
runtimeGraphDriver = s.runtime.StorageConfig().GraphDriverName
runtimeVolumePath = filepath.Clean(s.runtime.config.Engine.VolumePath)
)

// Some fields may be empty, indicating they are set to the default.
Expand Down Expand Up @@ -360,7 +361,7 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {

row := tx.QueryRow("SELECT Os, StaticDir, TmpDir, GraphRoot, RunRoot, GraphDriver, VolumeDir FROM DBConfig;")

if err := row.Scan(&os, &staticDir, &tmpDir, &graphRoot, &runRoot, &graphDriver, &volumePath); err != nil {
if err := row.Scan(&dbOS, &staticDir, &tmpDir, &graphRoot, &runRoot, &graphDriver, &volumePath); err != nil {
if errors.Is(err, sql.ErrNoRows) {
if _, err := tx.Exec(createRow, 1, schemaVersion, runtimeOS,
runtimeStaticDir, runtimeTmpDir, runtimeGraphRoot,
Expand All @@ -378,33 +379,52 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
return fmt.Errorf("retrieving DB config: %w", err)
}

checkField := func(fieldName, dbVal, ourVal string) error {
checkField := func(fieldName, dbVal, ourVal string, isPath bool) error {
if isPath {
// Evaluate symlinks. Ignore ENOENT. No guarantee all
// directories exist this early in Libpod init.
if dbVal != "" {
dbValClean, err := filepath.EvalSymlinks(dbVal)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("cannot evaluate symlinks on DB %s path %q: %w", fieldName, dbVal, err)
}
dbVal = dbValClean
}
if ourVal != "" {
ourValClean, err := filepath.EvalSymlinks(ourVal)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("cannot evaluate symlinks on our %s path %q: %w", fieldName, ourVal, err)
}
ourVal = ourValClean
}
}

if dbVal != ourVal {
return fmt.Errorf("database %s %q does not match our %s %q: %w", fieldName, dbVal, fieldName, ourVal, define.ErrDBBadConfig)
}

return nil
}

if err := checkField("os", os, runtimeOS); err != nil {
if err := checkField("os", dbOS, runtimeOS, false); err != nil {
return err
}
if err := checkField("static dir", staticDir, runtimeStaticDir); err != nil {
if err := checkField("static dir", staticDir, runtimeStaticDir, true); err != nil {
return err
}
if err := checkField("tmp dir", tmpDir, runtimeTmpDir); err != nil {
if err := checkField("tmp dir", tmpDir, runtimeTmpDir, true); err != nil {
return err
}
if err := checkField("graph root", graphRoot, runtimeGraphRoot); err != nil {
if err := checkField("graph root", graphRoot, runtimeGraphRoot, true); err != nil {
return err
}
if err := checkField("run root", runRoot, runtimeRunRoot); err != nil {
if err := checkField("run root", runRoot, runtimeRunRoot, true); err != nil {
return err
}
if err := checkField("graph driver", graphDriver, runtimeGraphDriver); err != nil {
if err := checkField("graph driver", graphDriver, runtimeGraphDriver, false); err != nil {
return err
}
if err := checkField("volume path", volumePath, runtimeVolumePath); err != nil {
if err := checkField("volume path", volumePath, runtimeVolumePath, true); err != nil {
return err
}

Expand Down
15 changes: 15 additions & 0 deletions test/system/005-info.bats
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ host.slirp4netns.executable | $expr_path
fi
}

@test "rootless podman with symlinked $HOME" {
# This is only needed as rootless, but we don't have a skip_if_root
# And it will not hurt to run as root.
skip_if_remote "path validation is only done in libpod, does not effect remote"

new_home=$PODMAN_TMPDIR/home

ln -s /home $new_home

# Just need the command to run cleanly
HOME=$PODMAN_TMPDIR/$HOME run_podman info

rm $new_home
}

@test "podman --root PATH --volumepath info - basic output" {
volumePath=${PODMAN_TMPDIR}/volumesGoHere
if ! is_remote; then
Expand Down

0 comments on commit 435258c

Please sign in to comment.