Skip to content

Commit

Permalink
Splits EACCES from EPERM on Windows (#1430)
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <adrian@tetrate.io>
  • Loading branch information
codefromthecrypt authored May 4, 2023
1 parent a912ea8 commit 6dd0cef
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 9 deletions.
6 changes: 4 additions & 2 deletions internal/platform/errno_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "syscall"
// See https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
const (
// ERROR_ACCESS_DENIED is a Windows error returned by syscall.Unlink
// instead of syscall.EPERM
// instead of syscall.EACCES
ERROR_ACCESS_DENIED = syscall.Errno(5)

// ERROR_INVALID_HANDLE is a Windows error returned by syscall.Write
Expand Down Expand Up @@ -59,7 +59,9 @@ func adjustErrno(err syscall.Errno) syscall.Errno {
return syscall.EEXIST
case ERROR_INVALID_HANDLE:
return syscall.EBADF
case ERROR_ACCESS_DENIED, ERROR_PRIVILEGE_NOT_HELD:
case ERROR_ACCESS_DENIED:
return syscall.EACCES
case ERROR_PRIVILEGE_NOT_HELD:
return syscall.EPERM
case ERROR_NEGATIVE_SEEK, ERROR_INVALID_NAME:
return syscall.EINVAL
Expand Down
5 changes: 1 addition & 4 deletions internal/platform/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,7 @@ func (f *fsFile) Chown(uid, gid int) syscall.Errno {

// Sync implements File.Sync
func (f *fsFile) Sync() syscall.Errno {
if f, ok := f.file.(syncFile); ok {
return UnwrapOSError(f.Sync())
}
return 0 // don't error
return sync(f.file)
}

// Datasync implements File.Datasync
Expand Down
12 changes: 10 additions & 2 deletions internal/platform/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@ func TestFsFileDatasync(t *testing.T) {
// similar to below. Effectively, this only tests that things don't error.
func testSync(t *testing.T, sync func(File) syscall.Errno) {
dPath := t.TempDir()
d, err := os.Open(dPath)
require.NoError(t, err)
defer d.Close()

// Even though it is invalid, try to sync a directory
errno := sync(NewFsFile(dPath, d))
require.EqualErrno(t, 0, errno)

fPath := path.Join(dPath, t.Name())

f := openFsFile(t, fPath, os.O_RDWR|os.O_CREATE, 0o600)
Expand All @@ -104,11 +112,11 @@ func testSync(t *testing.T, sync func(File) syscall.Errno) {
expected := "hello world!"

// Write the expected data
_, err := f.File().(io.Writer).Write([]byte(expected))
_, err = f.File().(io.Writer).Write([]byte(expected))
require.NoError(t, err)

// Sync the data.
errno := sync(f)
errno = sync(f)
require.EqualErrno(t, 0, errno)

// Rewind while the file is still open.
Expand Down
2 changes: 2 additions & 0 deletions internal/platform/sync.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !windows

package platform

import (
Expand Down
22 changes: 22 additions & 0 deletions internal/platform/sync_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package platform

import (
"io/fs"
"syscall"
)

func sync(f fs.File) syscall.Errno {
if s, ok := f.(syncFile); ok {
errno := UnwrapOSError(s.Sync())
// Coerce error performing stat on a directory to 0, as it won't work
// on Windows.
switch errno {
case syscall.EACCES /* Go 1.20 */, syscall.EBADF /* Go 1.18 */ :
if st, err := f.Stat(); err == nil && st.IsDir() {
errno = 0
}
}
return errno
}
return 0
}
2 changes: 1 addition & 1 deletion internal/platform/unlink_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func Unlink(name string) syscall.Errno {
return 0
}
errno := UnwrapOSError(err)
if errno == syscall.EPERM {
if errno == syscall.EACCES {
lstat, errLstat := os.Lstat(name)
if errLstat == nil && lstat.Mode()&os.ModeSymlink != 0 {
errno = UnwrapOSError(os.Remove(name))
Expand Down

0 comments on commit 6dd0cef

Please sign in to comment.