aboutsummaryrefslogtreecommitdiff
path: root/src/internal/syscall/windows
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/syscall/windows')
-rw-r--r--src/internal/syscall/windows/at_windows.go74
-rw-r--r--src/internal/syscall/windows/types_windows.go22
2 files changed, 96 insertions, 0 deletions
diff --git a/src/internal/syscall/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go
index 19bcc0dbac..edd2e42a88 100644
--- a/src/internal/syscall/windows/at_windows.go
+++ b/src/internal/syscall/windows/at_windows.go
@@ -254,3 +254,77 @@ func Deleteat(dirfd syscall.Handle, name string) error {
}
return 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 {
+ return err
+ }
+ var h syscall.Handle
+ err := NtOpenFile(
+ &h,
+ SYNCHRONIZE|DELETE,
+ objAttrs,
+ &IO_STATUS_BLOCK{},
+ FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT,
+ )
+ if err != nil {
+ return ntCreateFileError(err, 0)
+ }
+ defer syscall.CloseHandle(h)
+
+ renameInfoEx := FILE_RENAME_INFORMATION_EX{
+ Flags: FILE_RENAME_REPLACE_IF_EXISTS |
+ FILE_RENAME_POSIX_SEMANTICS,
+ RootDirectory: newdirfd,
+ }
+ p16, err := syscall.UTF16FromString(newpath)
+ if err != nil {
+ return err
+ }
+ if len(p16) > len(renameInfoEx.FileName) {
+ return syscall.EINVAL
+ }
+ copy(renameInfoEx.FileName[:], p16)
+ renameInfoEx.FileNameLength = uint32((len(p16) - 1) * 2)
+
+ const (
+ FileRenameInformation = 10
+ FileRenameInformationEx = 65
+ )
+ err = NtSetInformationFile(
+ h,
+ &IO_STATUS_BLOCK{},
+ uintptr(unsafe.Pointer(&renameInfoEx)),
+ uint32(unsafe.Sizeof(FILE_RENAME_INFORMATION_EX{})),
+ FileRenameInformationEx,
+ )
+ if err == nil {
+ return nil
+ }
+
+ // If the prior rename failed, the filesystem might not support
+ // POSIX semantics (for example, FAT), or might not have implemented
+ // FILE_RENAME_INFORMATION_EX.
+ //
+ // Try again.
+ renameInfo := FILE_RENAME_INFORMATION{
+ ReplaceIfExists: true,
+ RootDirectory: newdirfd,
+ }
+ copy(renameInfo.FileName[:], p16)
+ renameInfo.FileNameLength = renameInfoEx.FileNameLength
+
+ err = NtSetInformationFile(
+ h,
+ &IO_STATUS_BLOCK{},
+ uintptr(unsafe.Pointer(&renameInfo)),
+ uint32(unsafe.Sizeof(FILE_RENAME_INFORMATION{})),
+ FileRenameInformation,
+ )
+ if st, ok := err.(NTStatus); ok {
+ return st.Errno()
+ }
+ return err
+}
diff --git a/src/internal/syscall/windows/types_windows.go b/src/internal/syscall/windows/types_windows.go
index 6ae37afff8..718a4b863a 100644
--- a/src/internal/syscall/windows/types_windows.go
+++ b/src/internal/syscall/windows/types_windows.go
@@ -216,3 +216,25 @@ const (
FILE_DISPOSITION_ON_CLOSE = 0x00000008
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE = 0x00000010
)
+
+// Flags for FILE_RENAME_INFORMATION_EX.
+const (
+ FILE_RENAME_REPLACE_IF_EXISTS = 0x00000001
+ FILE_RENAME_POSIX_SEMANTICS = 0x00000002
+)
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_rename_information
+type FILE_RENAME_INFORMATION struct {
+ ReplaceIfExists bool
+ RootDirectory syscall.Handle
+ FileNameLength uint32
+ FileName [syscall.MAX_PATH]uint16
+}
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_rename_information
+type FILE_RENAME_INFORMATION_EX struct {
+ Flags uint32
+ RootDirectory syscall.Handle
+ FileNameLength uint32
+ FileName [syscall.MAX_PATH]uint16
+}