mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +08:00
Merge pull request #20874 from mheon/eval_symlinks_on_db_paths
Handle symlinks when checking DB vs runtime configs
This commit is contained in:
@ -4,7 +4,9 @@
|
|||||||
package libpod
|
package libpod
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -93,6 +95,7 @@ type dbConfigValidation struct {
|
|||||||
runtimeValue string
|
runtimeValue string
|
||||||
key []byte
|
key []byte
|
||||||
defaultValue string
|
defaultValue string
|
||||||
|
isPath bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the configuration of the database is compatible with the
|
// Check if the configuration of the database is compatible with the
|
||||||
@ -111,42 +114,49 @@ func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error {
|
|||||||
runtime.GOOS,
|
runtime.GOOS,
|
||||||
osKey,
|
osKey,
|
||||||
runtime.GOOS,
|
runtime.GOOS,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"libpod root directory (staticdir)",
|
"libpod root directory (staticdir)",
|
||||||
filepath.Clean(rt.config.Engine.StaticDir),
|
filepath.Clean(rt.config.Engine.StaticDir),
|
||||||
staticDirKey,
|
staticDirKey,
|
||||||
"",
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"libpod temporary files directory (tmpdir)",
|
"libpod temporary files directory (tmpdir)",
|
||||||
filepath.Clean(rt.config.Engine.TmpDir),
|
filepath.Clean(rt.config.Engine.TmpDir),
|
||||||
tmpDirKey,
|
tmpDirKey,
|
||||||
"",
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"storage temporary directory (runroot)",
|
"storage temporary directory (runroot)",
|
||||||
filepath.Clean(rt.StorageConfig().RunRoot),
|
filepath.Clean(rt.StorageConfig().RunRoot),
|
||||||
runRootKey,
|
runRootKey,
|
||||||
storeOpts.RunRoot,
|
storeOpts.RunRoot,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"storage graph root directory (graphroot)",
|
"storage graph root directory (graphroot)",
|
||||||
filepath.Clean(rt.StorageConfig().GraphRoot),
|
filepath.Clean(rt.StorageConfig().GraphRoot),
|
||||||
graphRootKey,
|
graphRootKey,
|
||||||
storeOpts.GraphRoot,
|
storeOpts.GraphRoot,
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"storage graph driver",
|
"storage graph driver",
|
||||||
rt.StorageConfig().GraphDriverName,
|
rt.StorageConfig().GraphDriverName,
|
||||||
graphDriverKey,
|
graphDriverKey,
|
||||||
storeOpts.GraphDriverName,
|
storeOpts.GraphDriverName,
|
||||||
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"volume path",
|
"volume path",
|
||||||
rt.config.Engine.VolumePath,
|
rt.config.Engine.VolumePath,
|
||||||
volPathKey,
|
volPathKey,
|
||||||
"",
|
"",
|
||||||
|
true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,22 +231,45 @@ func readOnlyValidateConfig(bucket *bolt.Bucket, toCheck dbConfigValidation) (bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbValue := string(keyBytes)
|
dbValue := string(keyBytes)
|
||||||
|
ourValue := toCheck.runtimeValue
|
||||||
|
|
||||||
if toCheck.runtimeValue != dbValue {
|
// 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 ourValue != dbValue {
|
||||||
// If the runtime value is the empty string and default is not,
|
// If the runtime value is the empty string and default is not,
|
||||||
// check against default.
|
// check against default.
|
||||||
if toCheck.runtimeValue == "" && toCheck.defaultValue != "" && dbValue == toCheck.defaultValue {
|
if ourValue == "" && toCheck.defaultValue != "" && dbValue == toCheck.defaultValue {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the DB value is the empty string, check that the runtime
|
// If the DB value is the empty string, check that the runtime
|
||||||
// value is the default.
|
// value is the default.
|
||||||
if dbValue == "" && toCheck.defaultValue != "" && toCheck.runtimeValue == toCheck.defaultValue {
|
if dbValue == "" && toCheck.defaultValue != "" && ourValue == toCheck.defaultValue {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, fmt.Errorf("database %s %q does not match our %s %q: %w",
|
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
|
return true, nil
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
goruntime "runtime"
|
goruntime "runtime"
|
||||||
@ -315,14 +316,14 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
|
|||||||
);`
|
);`
|
||||||
|
|
||||||
var (
|
var (
|
||||||
os, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
|
dbOS, staticDir, tmpDir, graphRoot, runRoot, graphDriver, volumePath string
|
||||||
runtimeOS = goruntime.GOOS
|
runtimeOS = goruntime.GOOS
|
||||||
runtimeStaticDir = filepath.Clean(s.runtime.config.Engine.StaticDir)
|
runtimeStaticDir = filepath.Clean(s.runtime.config.Engine.StaticDir)
|
||||||
runtimeTmpDir = filepath.Clean(s.runtime.config.Engine.TmpDir)
|
runtimeTmpDir = filepath.Clean(s.runtime.config.Engine.TmpDir)
|
||||||
runtimeGraphRoot = filepath.Clean(s.runtime.StorageConfig().GraphRoot)
|
runtimeGraphRoot = filepath.Clean(s.runtime.StorageConfig().GraphRoot)
|
||||||
runtimeRunRoot = filepath.Clean(s.runtime.StorageConfig().RunRoot)
|
runtimeRunRoot = filepath.Clean(s.runtime.StorageConfig().RunRoot)
|
||||||
runtimeGraphDriver = s.runtime.StorageConfig().GraphDriverName
|
runtimeGraphDriver = s.runtime.StorageConfig().GraphDriverName
|
||||||
runtimeVolumePath = filepath.Clean(s.runtime.config.Engine.VolumePath)
|
runtimeVolumePath = filepath.Clean(s.runtime.config.Engine.VolumePath)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Some fields may be empty, indicating they are set to the default.
|
// Some fields may be empty, indicating they are set to the default.
|
||||||
@ -359,7 +360,7 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
|
|||||||
|
|
||||||
row := tx.QueryRow("SELECT Os, StaticDir, TmpDir, GraphRoot, RunRoot, GraphDriver, VolumeDir FROM DBConfig;")
|
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 errors.Is(err, sql.ErrNoRows) {
|
||||||
if _, err := tx.Exec(createRow, 1, schemaVersion, runtimeOS,
|
if _, err := tx.Exec(createRow, 1, schemaVersion, runtimeOS,
|
||||||
runtimeStaticDir, runtimeTmpDir, runtimeGraphRoot,
|
runtimeStaticDir, runtimeTmpDir, runtimeGraphRoot,
|
||||||
@ -377,7 +378,26 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
|
|||||||
return fmt.Errorf("retrieving DB config: %w", err)
|
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 {
|
if dbVal != ourVal {
|
||||||
return fmt.Errorf("database %s %q does not match our %s %q: %w", fieldName, dbVal, fieldName, ourVal, define.ErrDBBadConfig)
|
return fmt.Errorf("database %s %q does not match our %s %q: %w", fieldName, dbVal, fieldName, ourVal, define.ErrDBBadConfig)
|
||||||
}
|
}
|
||||||
@ -385,25 +405,25 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkField("os", os, runtimeOS); err != nil {
|
if err := checkField("os", dbOS, runtimeOS, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := checkField("static dir", staticDir, runtimeStaticDir); err != nil {
|
if err := checkField("static dir", staticDir, runtimeStaticDir, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := checkField("tmp dir", tmpDir, runtimeTmpDir); err != nil {
|
if err := checkField("tmp dir", tmpDir, runtimeTmpDir, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := checkField("graph root", graphRoot, runtimeGraphRoot); err != nil {
|
if err := checkField("graph root", graphRoot, runtimeGraphRoot, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := checkField("run root", runRoot, runtimeRunRoot); err != nil {
|
if err := checkField("run root", runRoot, runtimeRunRoot, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := checkField("graph driver", graphDriver, runtimeGraphDriver); err != nil {
|
if err := checkField("graph driver", graphDriver, runtimeGraphDriver, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := checkField("volume path", volumePath, runtimeVolumePath); err != nil {
|
if err := checkField("volume path", volumePath, runtimeVolumePath, true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,6 +187,21 @@ host.slirp4netns.executable | $expr_path
|
|||||||
fi
|
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" {
|
@test "podman --root PATH --volumepath info - basic output" {
|
||||||
volumePath=${PODMAN_TMPDIR}/volumesGoHere
|
volumePath=${PODMAN_TMPDIR}/volumesGoHere
|
||||||
if ! is_remote; then
|
if ! is_remote; then
|
||||||
|
Reference in New Issue
Block a user