fix: directory listing on Go 1.20 windows (#16976)

This commit is contained in:
Klaus Post 2023-04-05 14:36:49 -07:00 committed by GitHub
parent ae011663e8
commit 62c3df0ca3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 46 deletions

View File

@ -20,13 +20,8 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
go-version: [1.20.x, 1.19.x] go-version: [1.20.x]
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest, windows-latest]
exclude:
- os: ubuntu-latest
go-version: 1.19.x
- os: windows-latest
go-version: 1.20.x
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-go@v3 - uses: actions/setup-go@v3

View File

@ -21,8 +21,8 @@
package cmd package cmd
import ( import (
"io"
"os" "os"
"path/filepath"
"syscall" "syscall"
) )
@ -39,38 +39,32 @@ func osMkdirAll(dirPath string, perm os.FileMode) error {
// the directory itself, if the dirPath doesn't exist this function doesn't return // the directory itself, if the dirPath doesn't exist this function doesn't return
// an error. // an error.
func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error) error { func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error) error {
f, err := Open(dirPath) // Ensure we don't pick up files as directories.
globAll := filepath.Clean(dirPath) + `\*`
globAllP, err := syscall.UTF16PtrFromString(globAll)
if err != nil { if err != nil {
if osErrToFileErr(err) == errFileNotFound { return errInvalidArgument
return nil
}
return osErrToFileErr(err)
} }
defer f.Close() data := &syscall.Win32finddata{}
handle, err := syscall.FindFirstFile(globAllP, data)
// Check if file or dir. This is the quickest way. if err != nil {
// Do not remove this check, on windows syscall.FindNextFile // Fails on file not found and when not a directory.
// would throw an exception if Fd() points to a file
// instead of a directory, we need to quickly fail
// in such situations - this workadound is expected.
if _, err = f.Seek(0, io.SeekStart); err == nil {
return errFileNotFound return errFileNotFound
} }
defer syscall.CloseHandle(handle)
data := &syscall.Win32finddata{} for ; ; err = syscall.FindNextFile(handle, data) {
for { if err != nil {
e := syscall.FindNextFile(syscall.Handle(f.Fd()), data) if err == syscall.ERROR_NO_MORE_FILES {
if e != nil {
if e == syscall.ERROR_NO_MORE_FILES {
break break
} else { } else {
if isSysErrPathNotFound(e) { if isSysErrPathNotFound(err) {
return nil return nil
} }
err = osErrToFileErr(&os.PathError{ err = osErrToFileErr(&os.PathError{
Op: "FindNextFile", Op: "FindNextFile",
Path: dirPath, Path: dirPath,
Err: e, Err: err,
}) })
if err == errFileNotFound { if err == errFileNotFound {
return nil return nil
@ -109,7 +103,7 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error)
typ = os.ModeDir typ = os.ModeDir
} }
if e = filter(name, typ); e == errDoneForNow { if err = filter(name, typ); err == errDoneForNow {
// filtering requested to return by caller. // filtering requested to return by caller.
return nil return nil
} }
@ -120,35 +114,30 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error)
// Return N entries at the directory dirPath. // Return N entries at the directory dirPath.
func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err error) { func readDirWithOpts(dirPath string, opts readDirOpts) (entries []string, err error) {
f, err := Open(dirPath) // Ensure we don't pick up files as directories.
globAll := filepath.Clean(dirPath) + `\*`
globAllP, err := syscall.UTF16PtrFromString(globAll)
if err != nil { if err != nil {
return nil, osErrToFileErr(err) return nil, errInvalidArgument
} }
defer f.Close() data := &syscall.Win32finddata{}
handle, err := syscall.FindFirstFile(globAllP, data)
// Check if file or dir. This is the quickest way. if err != nil {
// Do not remove this check, on windows syscall.FindNextFile // Fails on file not found and when not a directory.
// would throw an exception if Fd() points to a file
// instead of a directory, we need to quickly fail
// in such situations - this workadound is expected.
if _, err = f.Seek(0, io.SeekStart); err == nil {
return nil, errFileNotFound return nil, errFileNotFound
} }
defer syscall.CloseHandle(handle)
data := &syscall.Win32finddata{}
handle := syscall.Handle(f.Fd())
count := opts.count count := opts.count
for count != 0 { for ; count != 0; err = syscall.FindNextFile(handle, data) {
e := syscall.FindNextFile(handle, data) if err != nil {
if e != nil { if err == syscall.ERROR_NO_MORE_FILES {
if e == syscall.ERROR_NO_MORE_FILES {
break break
} else { } else {
return nil, osErrToFileErr(&os.PathError{ return nil, osErrToFileErr(&os.PathError{
Op: "FindNextFile", Op: "FindNextFile",
Path: dirPath, Path: dirPath,
Err: e, Err: err,
}) })
} }
} }