package explicitfilepath import ( "os" "path/filepath" "github.com/pkg/errors" ) // ResolvePathToFullyExplicit returns the input path converted to an absolute, no-symlinks, cleaned up path. // To do so, all elements of the input path must exist; as a special case, the final component may be // a non-existent name (but not a symlink pointing to a non-existent name) // This is intended as a a helper for implementations of types.ImageReference.PolicyConfigurationIdentity etc. func ResolvePathToFullyExplicit(path string) (string, error) { switch _, err := os.Lstat(path); { case err == nil: return resolveExistingPathToFullyExplicit(path) case os.IsNotExist(err): parent, file := filepath.Split(path) resolvedParent, err := resolveExistingPathToFullyExplicit(parent) if err != nil { return "", err } if file == "." || file == ".." { // Coverage: This can happen, but very rarely: if we have successfully resolved the parent, both "." and ".." in it should have been resolved as well. // This can still happen if there is a filesystem race condition, causing the Lstat() above to fail but the later resolution to succeed. // We do not care to promise anything if such filesystem race conditions can happen, but we definitely don't want to return "."/".." components // in the resulting path, and especially not at the end. return "", errors.Errorf("Unexpectedly missing special filename component in %s", path) } resolvedPath := filepath.Join(resolvedParent, file) // As a sanity check, ensure that there are no "." or ".." components. cleanedResolvedPath := filepath.Clean(resolvedPath) if cleanedResolvedPath != resolvedPath { // Coverage: This should never happen. return "", errors.Errorf("Internal inconsistency: Path %s resolved to %s still cleaned up to %s", path, resolvedPath, cleanedResolvedPath) } return resolvedPath, nil default: // err != nil, unrecognized return "", err } } // resolveExistingPathToFullyExplicit is the same as ResolvePathToFullyExplicit, // but without the special case for missing final component. func resolveExistingPathToFullyExplicit(path string) (string, error) { resolved, err := filepath.Abs(path) if err != nil { return "", err // Coverage: This can fail only if os.Getwd() fails. } resolved, err = filepath.EvalSymlinks(resolved) if err != nil { return "", err } return filepath.Clean(resolved), nil }