| // Copyright IBM Corp. 2014, 2026 | |
| // SPDX-License-Identifier: BUSL-1.1 | |
| //go:build windows | |
| // +build windows | |
| package replacefile | |
| import ( | |
| "os" | |
| "syscall" | |
| "golang.org/x/sys/windows" | |
| ) | |
| // AtomicRename renames from the source path to the destination path, | |
| // atomically replacing any file that might already exist at the destination. | |
| // | |
| // Typically this operation can succeed only if the source and destination | |
| // are within the same physical filesystem, so this function is best reserved | |
| // for cases where the source and destination exist in the same directory and | |
| // only the local filename differs between them. | |
| func AtomicRename(source, destination string) error { | |
| // On Windows, renaming one file over another is not atomic and certain | |
| // error conditions can result in having only the source file and nothing | |
| // at the destination file. Instead, we need to call into the MoveFileEx | |
| // Windows API function, setting two flags to opt in to replacing an | |
| // existing file. | |
| srcPtr, err := syscall.UTF16PtrFromString(source) | |
| if err != nil { | |
| return &os.LinkError{"replace", source, destination, err} | |
| } | |
| destPtr, err := syscall.UTF16PtrFromString(destination) | |
| if err != nil { | |
| return &os.LinkError{"replace", source, destination, err} | |
| } | |
| flags := uint32(windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH) | |
| err = windows.MoveFileEx(srcPtr, destPtr, flags) | |
| if err != nil { | |
| return &os.LinkError{"replace", source, destination, err} | |
| } | |
| return nil | |
| } |