Proposal Details
os.Remove on Windows uses syscall.DeleteFile, which cannot delete files that are currently open.
In Go 1.26 (https://go-review.googlesource.com/c/go/+/713480, b31dc77) os.RemoveAll attempts to use FILE_DISPOSITION_POSIX_SEMANTICS for Windows if available.
I propose we do something similar for os.Remove to make this consistent:
diff --git a/src/internal/syscall/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go
index f654e036ac..7020ee2766 100644
--- a/src/internal/syscall/windows/at_windows.go
+++ b/src/internal/syscall/windows/at_windows.go
@@ -319,6 +319,53 @@ func deleteatFallback(h syscall.Handle) error {
)
}
+func RemoveEx(path string) (fallback bool, err error) {
+ // Attempt to remove the file using POSIX semantics
+ // (which permit a file to be deleted while it is still open).
+ objAttrs := &OBJECT_ATTRIBUTES{}
+ if err := objAttrs.init(syscall.InvalidHandle, path); err != nil {
+ return true, err
+ }
+ var h syscall.Handle
+ err = NtOpenFile(
+ &h,
+ DELETE,
+ objAttrs,
+ &IO_STATUS_BLOCK{},
+ FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT,
+ )
+ if err != nil {
+ return true, err
+ }
+ defer syscall.CloseHandle(h)
+
+ const FileDispositionInformationEx = 64
+
+ err = NtSetInformationFile(
+ h,
+ &IO_STATUS_BLOCK{},
+ unsafe.Pointer(&FILE_DISPOSITION_INFORMATION_EX{
+ Flags: FILE_DISPOSITION_DELETE |
+ FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK |
+ FILE_DISPOSITION_POSIX_SEMANTICS |
+ FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
+ }),
+ uint32(unsafe.Sizeof(FILE_DISPOSITION_INFORMATION_EX{})),
+ FileDispositionInformationEx,
+ )
+ switch err {
+ case nil:
+ return false, nil
+ case STATUS_INVALID_INFO_CLASS,
+ STATUS_INVALID_PARAMETER,
+ STATUS_NOT_SUPPORTED:
+ return true, nil
+ default:
+ return false, err
+ }
+}
+
func Renameat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle, newpath string) error {
objAttrs := &OBJECT_ATTRIBUTES{}
if err := objAttrs.init(olddirfd, oldpath); err != nil {
diff --git a/src/os/file_windows.go b/src/os/file_windows.go
index ad172b5048..4480c7d8d3 100644
--- a/src/os/file_windows.go
+++ b/src/os/file_windows.go
@@ -220,6 +220,15 @@ func Truncate(name string, size int64) error {
// Remove removes the named file or directory.
// If there is an error, it will be of type [*PathError].
func Remove(name string) error {
+ // First try using POSIX semantics (allows deleting while open).
+ // This matches the behavior of Deleteat and os.Remove on Unix.
+ if fallback, err := windows.RemoveEx(name); err == nil {
+ return nil
+ } else if !fallback {
+ return &PathError{Op: "remove", Path: name, Err: err}
+ }
+
+ // Fall back to traditional DeleteFile/RemoveDirectory.
p, e := syscall.UTF16PtrFromString(fixLongPath(name))
if e != nil {
return &PathError{Op: "remove", Path: name, Err: e}
Proposal Details
os.Removeon Windows usessyscall.DeleteFile, which cannot delete files that are currently open.In Go 1.26 (https://go-review.googlesource.com/c/go/+/713480, b31dc77)
os.RemoveAllattempts to useFILE_DISPOSITION_POSIX_SEMANTICSfor Windows if available.I propose we do something similar for
os.Removeto make this consistent: