IsErrNoSpace compared against syscall.ENOSPC. Go defines that constant on Windows as a value in its application reserved range which no Windows API returns, so the comparison could never be true there. A full disk on Windows reports ERROR_DISK_FULL or ERROR_HANDLE_DISK_FULL instead. Preallocation failures were still caught, because those return a separate sentinel, but a disk that is already full fails at the directory creation or at the open long before preallocation is reached. That is the case reported. The errors are now held in a list which platform specific files add to in their init, which is the shape retriable_errors already uses in this package, and the comparison itself is unchanged. Windows appends the two codes that lib/file already recognises when preallocation fails. Every other platform keeps exactly the behaviour it had. This also reaches the VFS cache, which uses the same helper and has no preallocation path of its own, so its out of space handling has been inert on Windows.
60 lines
1.8 KiB
Go
60 lines
1.8 KiB
Go
//go:build windows
|
|
|
|
package fserrors
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
func TestIsErrNoSpaceRealWindowsError(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dirp, err := windows.UTF16PtrFromString(dir)
|
|
if err != nil {
|
|
t.Skipf("cannot convert the temporary directory path: %v", err)
|
|
}
|
|
var available, total, free uint64
|
|
if err := windows.GetDiskFreeSpaceEx(dirp, &available, &total, &free); err != nil {
|
|
t.Skipf("cannot read the free space of the temporary directory: %v", err)
|
|
}
|
|
|
|
f, err := os.Create(filepath.Join(dir, "truncate"))
|
|
require.NoError(t, err)
|
|
defer func() { require.NoError(t, f.Close()) }()
|
|
|
|
err = f.Truncate(int64(free + 1<<30))
|
|
if err == nil {
|
|
t.Skip("real Windows disk-full error coverage lost: volume did not enforce the free-space limit when truncating the file")
|
|
}
|
|
assert.True(t, IsErrNoSpace(err), "error = %v", err)
|
|
}
|
|
|
|
func TestIsErrNoSpaceWindows(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
err error
|
|
want bool
|
|
}{
|
|
{"syscall.ENOSPC", syscall.ENOSPC, true},
|
|
{"ERROR_DISK_FULL", windows.ERROR_DISK_FULL, true},
|
|
{"ERROR_HANDLE_DISK_FULL", windows.ERROR_HANDLE_DISK_FULL, true},
|
|
{"openat PathError", &os.PathError{Op: "openat", Path: "file", Err: windows.ERROR_DISK_FULL}, true},
|
|
{"mkdirat PathError", &os.PathError{Op: "mkdirat", Path: "dir", Err: windows.ERROR_DISK_FULL}, true},
|
|
{"SyscallError", os.NewSyscallError("write", windows.ERROR_HANDLE_DISK_FULL), true},
|
|
{"ERROR_ACCESS_DENIED", windows.ERROR_ACCESS_DENIED, false},
|
|
{"access denied PathError", &os.PathError{Op: "openat", Path: "file", Err: windows.ERROR_ACCESS_DENIED}, false},
|
|
{"nil", nil, false},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
assert.Equal(t, test.want, IsErrNoSpace(test.err))
|
|
})
|
|
}
|
|
}
|