From 62c3df0ca3c6089d42368bfb3768c167278b078e Mon Sep 17 00:00:00 2001 From: Klaus Post Date: Wed, 5 Apr 2023 14:36:49 -0700 Subject: [PATCH] fix: directory listing on Go 1.20 windows (#16976) --- .github/workflows/go-lint.yml | 7 +--- cmd/os_windows.go | 69 +++++++++++++++-------------------- 2 files changed, 30 insertions(+), 46 deletions(-) diff --git a/.github/workflows/go-lint.yml b/.github/workflows/go-lint.yml index 514ea6eb2..c47b5665f 100644 --- a/.github/workflows/go-lint.yml +++ b/.github/workflows/go-lint.yml @@ -20,13 +20,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - go-version: [1.20.x, 1.19.x] + go-version: [1.20.x] os: [ubuntu-latest, windows-latest] - exclude: - - os: ubuntu-latest - go-version: 1.19.x - - os: windows-latest - go-version: 1.20.x steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 diff --git a/cmd/os_windows.go b/cmd/os_windows.go index 0c2e236f9..ce9d1c2ee 100644 --- a/cmd/os_windows.go +++ b/cmd/os_windows.go @@ -21,8 +21,8 @@ package cmd import ( - "io" "os" + "path/filepath" "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 // an 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 osErrToFileErr(err) == errFileNotFound { - return nil - } - return osErrToFileErr(err) + return errInvalidArgument } - defer f.Close() - - // Check if file or dir. This is the quickest way. - // Do not remove this check, on windows syscall.FindNextFile - // 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 { + data := &syscall.Win32finddata{} + handle, err := syscall.FindFirstFile(globAllP, data) + if err != nil { + // Fails on file not found and when not a directory. return errFileNotFound } + defer syscall.CloseHandle(handle) - data := &syscall.Win32finddata{} - for { - e := syscall.FindNextFile(syscall.Handle(f.Fd()), data) - if e != nil { - if e == syscall.ERROR_NO_MORE_FILES { + for ; ; err = syscall.FindNextFile(handle, data) { + if err != nil { + if err == syscall.ERROR_NO_MORE_FILES { break } else { - if isSysErrPathNotFound(e) { + if isSysErrPathNotFound(err) { return nil } err = osErrToFileErr(&os.PathError{ Op: "FindNextFile", Path: dirPath, - Err: e, + Err: err, }) if err == errFileNotFound { return nil @@ -109,7 +103,7 @@ func readDirFn(dirPath string, filter func(name string, typ os.FileMode) error) typ = os.ModeDir } - if e = filter(name, typ); e == errDoneForNow { + if err = filter(name, typ); err == errDoneForNow { // filtering requested to return by caller. 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. 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 { - return nil, osErrToFileErr(err) + return nil, errInvalidArgument } - defer f.Close() - - // Check if file or dir. This is the quickest way. - // Do not remove this check, on windows syscall.FindNextFile - // 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 { + data := &syscall.Win32finddata{} + handle, err := syscall.FindFirstFile(globAllP, data) + if err != nil { + // Fails on file not found and when not a directory. return nil, errFileNotFound } - - data := &syscall.Win32finddata{} - handle := syscall.Handle(f.Fd()) + defer syscall.CloseHandle(handle) count := opts.count - for count != 0 { - e := syscall.FindNextFile(handle, data) - if e != nil { - if e == syscall.ERROR_NO_MORE_FILES { + for ; count != 0; err = syscall.FindNextFile(handle, data) { + if err != nil { + if err == syscall.ERROR_NO_MORE_FILES { break } else { return nil, osErrToFileErr(&os.PathError{ Op: "FindNextFile", Path: dirPath, - Err: e, + Err: err, }) } }