From 830e55007377646b51c1afef707b6a1d344424f8 Mon Sep 17 00:00:00 2001 From: Matt Heon Date: Tue, 2 Jul 2024 14:44:05 -0400 Subject: [PATCH] Ignore result of EvalSymlinks on ENOENT When the path does not exist, filepath.EvalSymlinks returns an empty string - so we can't just ignore ENOENT, we have to discard the result if an ENOENT is returned. Should fix Jira issue RHEL-37948 Signed-off-by: Matt Heon --- libpod/boltdb_state_internal.go | 16 ++++++---------- libpod/sqlite_state.go | 20 +++++++++++--------- libpod/util.go | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 3a2c66136f..a9b8d278bb 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -3,9 +3,7 @@ package libpod import ( - "errors" "fmt" - "io/fs" "os" "path/filepath" "runtime" @@ -237,20 +235,18 @@ func readOnlyValidateConfig(bucket *bolt.Bucket, toCheck dbConfigValidation) (bo // 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) { + checkedVal, err := evalSymlinksIfExists(dbValue) + if err != nil { return false, fmt.Errorf("evaluating symlinks on DB %s path %q: %w", toCheck.name, dbValue, err) } - dbValue = dbVal + dbValue = checkedVal } if ourValue != "" { - ourVal, err := filepath.EvalSymlinks(ourValue) - if err != nil && !errors.Is(err, fs.ErrNotExist) { + checkedVal, err := evalSymlinksIfExists(ourValue) + if err != nil { return false, fmt.Errorf("evaluating symlinks on configured %s path %q: %w", toCheck.name, ourValue, err) } - ourValue = ourVal + ourValue = checkedVal } } diff --git a/libpod/sqlite_state.go b/libpod/sqlite_state.go index bee2210648..912570fa7b 100644 --- a/libpod/sqlite_state.go +++ b/libpod/sqlite_state.go @@ -6,7 +6,6 @@ import ( "database/sql" "errors" "fmt" - "io/fs" "os" "path/filepath" goruntime "runtime" @@ -379,21 +378,24 @@ func (s *SQLiteState) ValidateDBConfig(runtime *Runtime) (defErr 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. + // 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. + // Ignore ENOENT as reasonable, as some paths may not exist in early Libpod + // init. if dbVal != "" { - dbValClean, err := filepath.EvalSymlinks(dbVal) - if err != nil && !errors.Is(err, fs.ErrNotExist) { + checkedVal, err := evalSymlinksIfExists(dbVal) + if err != nil { return fmt.Errorf("cannot evaluate symlinks on DB %s path %q: %w", fieldName, dbVal, err) } - dbVal = dbValClean + dbVal = checkedVal } if ourVal != "" { - ourValClean, err := filepath.EvalSymlinks(ourVal) - if err != nil && !errors.Is(err, fs.ErrNotExist) { + checkedVal, err := evalSymlinksIfExists(ourVal) + if err != nil { return fmt.Errorf("cannot evaluate symlinks on our %s path %q: %w", fieldName, ourVal, err) } - ourVal = ourValClean + ourVal = checkedVal } } diff --git a/libpod/util.go b/libpod/util.go index 195dbeee16..21d864d98d 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "io" + "io/fs" "net/http" "os" "path/filepath" @@ -288,3 +289,22 @@ func writeStringToPath(path, contents, mountLabel string, uid, gid int) error { return nil } + +// If the given path exists, evaluate any symlinks in it. If it does not, clean +// the path and return it. Used to try and verify path equality in a somewhat +// sane fashion. +func evalSymlinksIfExists(toCheck string) (string, error) { + checkedVal, err := filepath.EvalSymlinks(toCheck) + if err != nil { + // If the error is not ENOENT, something more serious has gone + // wrong, return it. + if !errors.Is(err, fs.ErrNotExist) { + return "", err + } + // This is an ENOENT. On ENOENT, EvalSymlinks returns "". + // We don't want that. Return a cleaned version of the original + // path. + return filepath.Clean(toCheck), nil + } + return checkedVal, nil +}