Fix symlink behaviour (#1020)

* Fix symlink behaviour

Make sure to evaluate symlinks when the path is not absolute and not
local. Otherwise just fall back to regular absolute paths.

Signed-off-by: Zoltán Reegn <zoltan.reegn@gmail.com>
This commit is contained in:
Zoltán Reegn 2023-09-13 15:14:50 +02:00 committed by GitHub
parent ad258463b6
commit 4fb150238b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 95 additions and 21 deletions

View File

@ -34,17 +34,18 @@ type FileSystem struct {
Getwd func() (string, error) Getwd func() (string, error)
Chdir func(string) error Chdir func(string) error
Abs func(string) (string, error) Abs func(string) (string, error)
EvalSymlinks func(string) (string, error)
} }
func DefaultFileSystem() *FileSystem { func DefaultFileSystem() *FileSystem {
dfs := FileSystem{ dfs := FileSystem{
ReadDir: os.ReadDir, ReadDir: os.ReadDir,
DeleteFile: os.Remove, DeleteFile: os.Remove,
Stat: os.Stat, Stat: os.Stat,
Glob: filepath.Glob, Glob: filepath.Glob,
Getwd: os.Getwd, Getwd: os.Getwd,
Chdir: os.Chdir, Chdir: os.Chdir,
Abs: filepath.Abs, EvalSymlinks: filepath.EvalSymlinks,
} }
dfs.Stat = dfs.stat dfs.Stat = dfs.stat
@ -52,6 +53,7 @@ func DefaultFileSystem() *FileSystem {
dfs.FileExistsAt = dfs.fileExistsAtDefault dfs.FileExistsAt = dfs.fileExistsAtDefault
dfs.DirectoryExistsAt = dfs.directoryExistsDefault dfs.DirectoryExistsAt = dfs.directoryExistsDefault
dfs.FileExists = dfs.fileExistsDefault dfs.FileExists = dfs.fileExistsDefault
dfs.Abs = dfs.absDefault
return &dfs return &dfs
} }
@ -91,7 +93,9 @@ func FromFileSystem(params FileSystem) *FileSystem {
if params.Abs != nil { if params.Abs != nil {
dfs.Abs = params.Abs dfs.Abs = params.Abs
} }
if params.EvalSymlinks != nil {
dfs.EvalSymlinks = params.EvalSymlinks
}
return dfs return dfs
} }
@ -110,13 +114,20 @@ func (filesystem *FileSystem) readFile(name string) ([]byte, error) {
} }
func (filesystem *FileSystem) fileExistsAtDefault(path string) bool { func (filesystem *FileSystem) fileExistsAtDefault(path string) bool {
path, err := filesystem.resolveSymlinks(path)
if err != nil {
return false
}
fileInfo, err := filesystem.Stat(path) fileInfo, err := filesystem.Stat(path)
return err == nil && fileInfo.Mode().IsRegular() return err == nil && fileInfo.Mode().IsRegular()
} }
func (filesystem *FileSystem) fileExistsDefault(path string) (bool, error) { func (filesystem *FileSystem) fileExistsDefault(path string) (bool, error) {
_, err := filesystem.Stat(path) path, err := filesystem.resolveSymlinks(path)
if err != nil {
return false, err
}
_, err = filesystem.Stat(path)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false, nil return false, nil
@ -127,6 +138,38 @@ func (filesystem *FileSystem) fileExistsDefault(path string) (bool, error) {
} }
func (filesystem *FileSystem) directoryExistsDefault(path string) bool { func (filesystem *FileSystem) directoryExistsDefault(path string) bool {
path, err := filesystem.resolveSymlinks(path)
if err != nil {
return false
}
fileInfo, err := filesystem.Stat(path) fileInfo, err := filesystem.Stat(path)
return err == nil && fileInfo.Mode().IsDir() return err == nil && fileInfo.Mode().IsDir()
} }
func (filesystem *FileSystem) resolveSymlinks(path string) (string, error) {
if !filepath.IsAbs(path) && !filepath.IsLocal(path) {
basePath, err := filesystem.Getwd()
if err != nil {
return "", err
}
basePath, err = filesystem.EvalSymlinks(basePath)
if err != nil {
return "", err
}
path, err := filesystem.EvalSymlinks(filepath.Join(basePath, path))
if err != nil {
return "", err
}
return path, nil
}
return path, nil
}
func (filesystem *FileSystem) absDefault(path string) (string, error) {
path, err := filesystem.resolveSymlinks(path)
if err != nil {
return "", err
}
return filepath.Abs(path)
}

View File

@ -12,28 +12,58 @@ import (
func NewTestFileSystem() FileSystem { func NewTestFileSystem() FileSystem {
replaceffs := FileSystem{ replaceffs := FileSystem{
Stat: func(s string) (os.FileInfo, error) { Stat: func(s string) (os.FileInfo, error) {
if strings.HasPrefix(s, "existing_file") { if strings.HasSuffix(s, "existing_file.txt") {
return fileStat{mode: 0}, nil return fileStat{mode: 0}, nil
} }
if strings.HasPrefix(s, "existing_dir") { if strings.HasSuffix(s, "existing_dir") {
return fileStat{mode: fs.ModeDir}, nil return fileStat{mode: fs.ModeDir}, nil
} }
return nil, errors.New("Error") return nil, errors.New("Error")
}, },
Getwd: func() (string, error) {
return "/test/dir", nil
},
EvalSymlinks: func(s string) (string, error) {
if s == "/test/dir" {
return "/real/dir", nil
} else {
return s, nil
}
},
} }
return *FromFileSystem(replaceffs) return *FromFileSystem(replaceffs)
} }
func TestFs_resolveSymlinks(t *testing.T) {
ffs := NewTestFileSystem()
path, _ := ffs.resolveSymlinks("../existing_file.txt")
if path != "/real/existing_file.txt" {
t.Errorf("Expected absolute path %s but got %s", "/real/existing_file.txt", path)
}
path, _ = ffs.resolveSymlinks("./existing_file.txt")
if path != "./existing_file.txt" {
t.Errorf("Expected local path %s but got %s", "./existing_file.txt", path)
}
path, _ = ffs.resolveSymlinks("existing_file.txt")
if path != "existing_file.txt" {
t.Errorf("Expected local path %s but got %s", "existing_file.txt", path)
}
path, _ = ffs.resolveSymlinks("/a/b/c/existing_file.txt")
if path != "/a/b/c/existing_file.txt" {
t.Errorf("Expected absolute path %s but got %s", "/a/b/c/existing_file.txt", path)
}
}
func TestFs_fileExistsDefault(t *testing.T) { func TestFs_fileExistsDefault(t *testing.T) {
ffs := NewTestFileSystem() ffs := NewTestFileSystem()
var exists, _ = ffs.FileExists("existing_file.txt") exists, _ := ffs.FileExists("existing_file.txt")
if !exists { if !exists {
t.Errorf("Expected file %s, not found", "existing_file.txt") t.Errorf("Expected file %s, not found", "existing_file.txt")
} }
exists, _ = ffs.FileExists("non_existing_file.txt") exists, _ = ffs.FileExists("missing_file.txt")
if exists { if exists {
t.Errorf("Not expected file %s, found", "non_existing_file.txt") t.Errorf("Not expected file %s, found", "missing_file.txt")
} }
dfs := DefaultFileSystem() dfs := DefaultFileSystem()
@ -46,14 +76,14 @@ func TestFs_fileExistsDefault(t *testing.T) {
func TestFs_fileExistsAtDefault(t *testing.T) { func TestFs_fileExistsAtDefault(t *testing.T) {
ffs := NewTestFileSystem() ffs := NewTestFileSystem()
var exists = ffs.FileExistsAt("existing_file.txt") exists := ffs.FileExistsAt("existing_file.txt")
if !exists { if !exists {
t.Errorf("Expected file %s, not found", "existing_file.txt") t.Errorf("Expected file %s, not found", "existing_file.txt")
} }
exists = ffs.FileExistsAt("non_existing_file.txt") exists = ffs.FileExistsAt("missing_file.txt")
if exists { if exists {
t.Errorf("Not expected file %s, found", "non_existing_file.txt") t.Errorf("Not expected file %s, found", "missing_file.txt")
} }
exists = ffs.FileExistsAt("existing_dir") exists = ffs.FileExistsAt("existing_dir")
@ -70,12 +100,12 @@ func TestFs_fileExistsAtDefault(t *testing.T) {
func TestFs_directoryExistsDefault(t *testing.T) { func TestFs_directoryExistsDefault(t *testing.T) {
ffs := NewTestFileSystem() ffs := NewTestFileSystem()
var exists = ffs.DirectoryExistsAt("existing_dir") exists := ffs.DirectoryExistsAt("existing_dir")
if !exists { if !exists {
t.Errorf("Expected file %s, not found", "existing_dir") t.Errorf("Expected file %s, not found", "existing_dir")
} }
exists = ffs.DirectoryExistsAt("not_existing_dir") exists = ffs.DirectoryExistsAt("missing_dir")
if exists { if exists {
t.Errorf("Not expected file %s, found", "existing_dir") t.Errorf("Not expected file %s, found", "existing_dir")
} }
@ -148,7 +178,8 @@ func TestFs_DefaultBuilder(t *testing.T) {
ffs.Stat == nil || ffs.Stat == nil ||
ffs.Getwd == nil || ffs.Getwd == nil ||
ffs.Chdir == nil || ffs.Chdir == nil ||
ffs.Abs == nil { ffs.Abs == nil ||
ffs.EvalSymlinks == nil {
t.Errorf("Missing functions in DefaultFileSystem") t.Errorf("Missing functions in DefaultFileSystem")
} }
} }