-
Notifications
You must be signed in to change notification settings - Fork 229
Description
We've upgraded to Go 1.25 in #10156, but ended up having to revert to Go 1.24's implementation of os.RemoveAll in the implementation of the uninstall command.
We need to diagnose what the root cause of the problem is and ideally make the uninstall process less sensitive to the implementation details of Go's file operations on Windows.
I'm going to update this issue with a more detailed account of the troubleshooting I've done later. As far as I can tell, the problem is that our approach to removing an executable which is actively being used is a trick involving NTFS Alternate Data Streams. This trick doesn't work with the new syscalls used by Go 1.25's os.RemoveAll implementation.
Why the ADS trick doesn't work anymore on Go 1.25
Go 1.25 introduced a new Windows implementation of os.RemoveAll in os/removeall_at.go, replacing the older path-based approach. For each file it encounters, it calls removefileat, which delegates to internal/syscall/windows.Deleteat.
Deleteat calls NtSetInformationFile with the flag combination at at_windows.go:297–303:
FILE_DISPOSITION_DELETE
| FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK // ← new in Go 1.25
| FILE_DISPOSITION_POSIX_SEMANTICS
| FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE
The critical addition is FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK. This flag instructs the NT kernel to check whether the file has any active image sections — i.e., whether the file is a running executable — and return STATUS_ACCESS_DENIED if so.
If set, indicates the system SHOULD fail deleting the file if an image section exists. If not set and the FILE_DISPOSITION_POSIX_SEMANTICS flag is set; indicates the file can be deleted even if it has an image section. This flag was added to support backward compatibility with the existing behavior of the FileDispositionInformation (see section [2.4.11](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/12c3dd1c-14f6-4229-9d29-75fb2cb392f6)) operation.
Go 1.24's os.Remove calls DeleteFileW, which uses the old FileDispositionInformation API — a simple delete-on-close mark with no image-section check. Because FILE_DISPOSITION_FORCE_IMAGE_SECTION_CHECK only exists in the newer FileDispositionInformationEx class, DeleteFileW is unaffected by it and can delete a running executable with an Alternate Data Stream.