Skip to content

Commit a701f47

Browse files
committed
Fix Symlink evaluation on Windows
1 parent 26bb051 commit a701f47

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

discovery.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ func (s *PHPStore) discoverPHPViaPHP(dir, binName string) *Version {
191191
}
192192
php = filepath.Clean(php)
193193
var err error
194-
php, err = filepath.EvalSymlinks(php)
194+
php, err = evalSymlinks(php)
195195
if err != nil {
196196
s.log(" %s is not a valid symlink", php)
197197
return nil
@@ -332,7 +332,7 @@ func (s *PHPStore) pathDirectories(configDir string) []string {
332332
seen := make(map[string]bool)
333333
for _, dir := range filepath.SplitList(path) {
334334
dir = strings.Replace(dir, "%%USERPROFILE%%", user, 1)
335-
edir, err := filepath.EvalSymlinks(dir)
335+
edir, err := evalSymlinks(dir)
336336
if err != nil {
337337
continue
338338
}

fsutils_others.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//go:build !windows
2+
3+
package phpstore
4+
5+
import "path/filepath"
6+
7+
var evalSymlinks = filepath.EvalSymlinks

fsutils_windows.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package phpstore
2+
3+
import (
4+
"errors"
5+
"os"
6+
"path/filepath"
7+
"syscall"
8+
)
9+
10+
// Taken from https://github.com/golangci/golangci-lint/blob/main/pkg/fsutils/fsutils_windows.go
11+
12+
// This is a workaround for the behavior of [filepath.EvalSymlinks],
13+
// which fails with [syscall.ENOTDIR] if the specified path contains a junction on Windows.
14+
// Junctions can occur, for example, when a volume is mounted as a subdirectory inside another drive.
15+
// This can usually happen when using the Dev Drives feature and replacing existing directories.
16+
// See: https://github.com/golang/go/issues/40180
17+
//
18+
// Since [syscall.ENOTDIR] is only returned when calling [filepath.EvalSymlinks] on Windows
19+
// if part of the presented path is a junction and nothing before was a symlink,
20+
// we simply treat this as NOT symlink,
21+
// because a symlink over the junction makes no sense at all.
22+
func evalSymlinks(path string) (string, error) {
23+
resolved, err := filepath.EvalSymlinks(path)
24+
if err == nil {
25+
return resolved, nil
26+
}
27+
28+
if !errors.Is(err, syscall.ENOTDIR) {
29+
return "", err
30+
}
31+
32+
_, err = os.Stat(path)
33+
if err != nil {
34+
return "", err
35+
}
36+
37+
// If exists, we make the path cleaned before being used
38+
return filepath.Abs(path), nil
39+
}

store.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ func (s *PHPStore) loadVersions() {
238238
// addVersion ensures that all versions are unique in the store
239239
func (s *PHPStore) addVersion(version *Version) int {
240240
idx, ok := s.seen[version.PHPPath]
241-
sl, _ := filepath.EvalSymlinks(version.PHPPath)
241+
sl, _ := evalSymlinks(version.PHPPath)
242242
// double-check to see if that's not just a symlink to another existing version
243243
if !ok && sl != "" {
244244
idx, ok = s.seen[sl]

version.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,35 +185,35 @@ func (v *Version) setServer(fpm, cgi, phpconfig, phpize, phpdbg string) string {
185185
msg := fmt.Sprintf(" Found PHP: %s", v.PHPPath)
186186
fpm = filepath.Clean(fpm)
187187
if _, err := os.Stat(fpm); err == nil {
188-
if fpm, err := filepath.EvalSymlinks(fpm); err == nil {
188+
if fpm, err := evalSymlinks(fpm); err == nil {
189189
v.FPMPath = fpm
190190
msg += fmt.Sprintf(", with FPM: %s", fpm)
191191
}
192192
}
193193
cgi = filepath.Clean(cgi)
194194
if _, err := os.Stat(cgi); err == nil {
195-
if cgi, err := filepath.EvalSymlinks(cgi); err == nil {
195+
if cgi, err := evalSymlinks(cgi); err == nil {
196196
v.CGIPath = cgi
197197
msg += fmt.Sprintf(", with CGI: %s", cgi)
198198
}
199199
}
200200
phpconfig = filepath.Clean(phpconfig)
201201
if _, err := os.Stat(phpconfig); err == nil {
202-
if phpconfig, err := filepath.EvalSymlinks(phpconfig); err == nil {
202+
if phpconfig, err := evalSymlinks(phpconfig); err == nil {
203203
v.PHPConfigPath = phpconfig
204204
msg += fmt.Sprintf(", with php-config: %s", phpconfig)
205205
}
206206
}
207207
phpize = filepath.Clean(phpize)
208208
if _, err := os.Stat(phpize); err == nil {
209-
if phpize, err := filepath.EvalSymlinks(phpize); err == nil {
209+
if phpize, err := evalSymlinks(phpize); err == nil {
210210
v.PHPizePath = phpize
211211
msg += fmt.Sprintf(", with phpize: %s", phpize)
212212
}
213213
}
214214
phpdbg = filepath.Clean(phpdbg)
215215
if _, err := os.Stat(phpdbg); err == nil {
216-
if phpdbg, err := filepath.EvalSymlinks(phpdbg); err == nil {
216+
if phpdbg, err := evalSymlinks(phpdbg); err == nil {
217217
v.PHPdbgPath = phpdbg
218218
msg += fmt.Sprintf(", with phpdbg: %s", phpdbg)
219219
}

0 commit comments

Comments
 (0)