mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-11 21:49:13 +00:00
Fix WAL flakiness on Windows (#253)
This commit is contained in:
@@ -1,16 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package osutil
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// OpenFile behaves the same as [os.OpenFile],
|
||||
// except on Windows it sets [syscall.FILE_SHARE_DELETE].
|
||||
//
|
||||
// See: https://go.dev/issue/32088#issuecomment-502850674
|
||||
func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package osutil
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
. "syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// OpenFile behaves the same as [os.OpenFile],
|
||||
// except on Windows it sets [syscall.FILE_SHARE_DELETE].
|
||||
//
|
||||
// See: https://go.dev/issue/32088#issuecomment-502850674
|
||||
func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
if name == "" {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: ENOENT}
|
||||
}
|
||||
r, err := syscallOpen(name, flag|O_CLOEXEC, uint32(perm.Perm()))
|
||||
if err != nil {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: err}
|
||||
}
|
||||
return os.NewFile(uintptr(r), name), nil
|
||||
}
|
||||
|
||||
// syscallOpen is a copy of [syscall.Open]
|
||||
// that uses [syscall.FILE_SHARE_DELETE],
|
||||
// and supports [syscall.FILE_FLAG_OVERLAPPED].
|
||||
//
|
||||
// https://go.dev/src/syscall/syscall_windows.go
|
||||
func syscallOpen(name string, flag int, perm uint32) (fd Handle, err error) {
|
||||
if len(name) == 0 {
|
||||
return InvalidHandle, ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
namep, err := UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return InvalidHandle, err
|
||||
}
|
||||
var access uint32
|
||||
switch flag & (O_RDONLY | O_WRONLY | O_RDWR) {
|
||||
case O_RDONLY:
|
||||
access = GENERIC_READ
|
||||
case O_WRONLY:
|
||||
access = GENERIC_WRITE
|
||||
case O_RDWR:
|
||||
access = GENERIC_READ | GENERIC_WRITE
|
||||
}
|
||||
if flag&O_CREAT != 0 {
|
||||
access |= GENERIC_WRITE
|
||||
}
|
||||
if flag&O_APPEND != 0 {
|
||||
// Remove GENERIC_WRITE unless O_TRUNC is set, in which case we need it to truncate the file.
|
||||
// We can't just remove FILE_WRITE_DATA because GENERIC_WRITE without FILE_WRITE_DATA
|
||||
// starts appending at the beginning of the file rather than at the end.
|
||||
if flag&O_TRUNC == 0 {
|
||||
access &^= GENERIC_WRITE
|
||||
}
|
||||
// Set all access rights granted by GENERIC_WRITE except for FILE_WRITE_DATA.
|
||||
access |= FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | _FILE_WRITE_EA | STANDARD_RIGHTS_WRITE | SYNCHRONIZE
|
||||
}
|
||||
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
|
||||
var sa *SecurityAttributes
|
||||
if flag&O_CLOEXEC == 0 {
|
||||
sa = makeInheritSa()
|
||||
}
|
||||
// We don't use CREATE_ALWAYS, because when opening a file with
|
||||
// FILE_ATTRIBUTE_READONLY these will replace an existing file
|
||||
// with a new, read-only one. See https://go.dev/issue/38225.
|
||||
//
|
||||
// Instead, we ftruncate the file after opening when O_TRUNC is set.
|
||||
var createmode uint32
|
||||
switch {
|
||||
case flag&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
createmode = CREATE_NEW
|
||||
case flag&O_CREAT == O_CREAT:
|
||||
createmode = OPEN_ALWAYS
|
||||
default:
|
||||
createmode = OPEN_EXISTING
|
||||
}
|
||||
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
||||
if perm&S_IWRITE == 0 {
|
||||
attrs = FILE_ATTRIBUTE_READONLY
|
||||
}
|
||||
if flag&O_WRONLY == 0 && flag&O_RDWR == 0 {
|
||||
// We might be opening or creating a directory.
|
||||
// CreateFile requires FILE_FLAG_BACKUP_SEMANTICS
|
||||
// to work with directories.
|
||||
attrs |= FILE_FLAG_BACKUP_SEMANTICS
|
||||
}
|
||||
if flag&O_SYNC != 0 {
|
||||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||
}
|
||||
if flag&O_NONBLOCK != 0 {
|
||||
attrs |= FILE_FLAG_OVERLAPPED
|
||||
}
|
||||
h, err := CreateFile(namep, access, sharemode, sa, createmode, attrs, 0)
|
||||
if h == InvalidHandle {
|
||||
if err == ERROR_ACCESS_DENIED && (flag&O_WRONLY != 0 || flag&O_RDWR != 0) {
|
||||
// We should return EISDIR when we are trying to open a directory with write access.
|
||||
fa, e1 := GetFileAttributes(namep)
|
||||
if e1 == nil && fa&FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
err = EISDIR
|
||||
}
|
||||
}
|
||||
return h, err
|
||||
}
|
||||
// Ignore O_TRUNC if the file has just been created.
|
||||
if flag&O_TRUNC == O_TRUNC &&
|
||||
(createmode == OPEN_EXISTING || (createmode == OPEN_ALWAYS /*&& err == ERROR_ALREADY_EXISTS*/)) {
|
||||
err = Ftruncate(h, 0)
|
||||
if err != nil {
|
||||
CloseHandle(h)
|
||||
return InvalidHandle, err
|
||||
}
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func makeInheritSa() *SecurityAttributes {
|
||||
var sa SecurityAttributes
|
||||
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||
sa.InheritHandle = 1
|
||||
return &sa
|
||||
}
|
||||
|
||||
const _FILE_WRITE_EA = 0x00000010
|
||||
@@ -1,3 +1,4 @@
|
||||
// Package osutil implements operating system utilities.
|
||||
package osutil
|
||||
|
||||
import (
|
||||
@@ -19,7 +20,7 @@ type FS struct{}
|
||||
|
||||
// Open implements [fs.FS].
|
||||
func (FS) Open(name string) (fs.File, error) {
|
||||
return OpenFile(name, os.O_RDONLY, 0)
|
||||
return os.OpenFile(name, os.O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// ReadFileFS implements [fs.StatFS].
|
||||
@@ -31,3 +32,10 @@ func (FS) Stat(name string) (fs.FileInfo, error) {
|
||||
func (FS) ReadFile(name string) ([]byte, error) {
|
||||
return os.ReadFile(name)
|
||||
}
|
||||
|
||||
// OpenFile behaves the same as [os.OpenFile].
|
||||
//
|
||||
// Deprecated: use os.OpenFile instead.
|
||||
func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// Package osutil implements operating system utilities.
|
||||
package osutil
|
||||
Reference in New Issue
Block a user