aboutsummaryrefslogtreecommitdiff
path: root/src/internal
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal')
-rw-r--r--src/internal/syscall/unix/asm_darwin.s1
-rw-r--r--src/internal/syscall/unix/asm_openbsd.s2
-rw-r--r--src/internal/syscall/unix/at.go22
-rw-r--r--src/internal/syscall/unix/at_darwin.go26
-rw-r--r--src/internal/syscall/unix/at_libc.go26
-rw-r--r--src/internal/syscall/unix/at_openbsd.go26
-rw-r--r--src/internal/syscall/unix/at_solaris.go1
-rw-r--r--src/internal/syscall/unix/at_sysnum_dragonfly.go1
-rw-r--r--src/internal/syscall/unix/at_sysnum_freebsd.go1
-rw-r--r--src/internal/syscall/unix/at_sysnum_netbsd.go1
-rw-r--r--src/internal/syscall/unix/at_wasip1.go18
-rw-r--r--src/internal/syscall/unix/renameat2_sysnum_linux.go16
-rw-r--r--src/internal/syscall/unix/renameat_sysnum_linux.go13
-rw-r--r--src/internal/syscall/windows/at_windows.go74
-rw-r--r--src/internal/syscall/windows/types_windows.go22
15 files changed, 249 insertions, 1 deletions
diff --git a/src/internal/syscall/unix/asm_darwin.s b/src/internal/syscall/unix/asm_darwin.s
index 0f28cd1e39..a72240f512 100644
--- a/src/internal/syscall/unix/asm_darwin.s
+++ b/src/internal/syscall/unix/asm_darwin.s
@@ -27,3 +27,4 @@ TEXT ·libc_readlinkat_trampoline(SB),NOSPLIT,$0-0; JMP libc_readlinkat(SB)
TEXT ·libc_mkdirat_trampoline(SB),NOSPLIT,$0-0; JMP libc_mkdirat(SB)
TEXT ·libc_fchmodat_trampoline(SB),NOSPLIT,$0-0; JMP libc_fchmodat(SB)
TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0; JMP libc_fchownat(SB)
+TEXT ·libc_renameat_trampoline(SB),NOSPLIT,$0-0; JMP libc_renameat(SB)
diff --git a/src/internal/syscall/unix/asm_openbsd.s b/src/internal/syscall/unix/asm_openbsd.s
index b804a52714..2b88b6988c 100644
--- a/src/internal/syscall/unix/asm_openbsd.s
+++ b/src/internal/syscall/unix/asm_openbsd.s
@@ -18,3 +18,5 @@ TEXT ·libc_fchmodat_trampoline(SB),NOSPLIT,$0-0
JMP libc_fchmodat(SB)
TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0
JMP libc_fchownat(SB)
+TEXT ·libc_renameat_trampoline(SB),NOSPLIT,$0-0
+ JMP libc_renameat(SB)
diff --git a/src/internal/syscall/unix/at.go b/src/internal/syscall/unix/at.go
index 794f8ace14..be7920c115 100644
--- a/src/internal/syscall/unix/at.go
+++ b/src/internal/syscall/unix/at.go
@@ -114,3 +114,25 @@ func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
}
return nil
}
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error {
+ oldp, err := syscall.BytePtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ newp, err := syscall.BytePtrFromString(newpath)
+ if err != nil {
+ return err
+ }
+ _, _, errno := syscall.Syscall6(renameatTrap,
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ 0,
+ 0)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/internal/syscall/unix/at_darwin.go b/src/internal/syscall/unix/at_darwin.go
index 75d7b45569..4f39b76ad1 100644
--- a/src/internal/syscall/unix/at_darwin.go
+++ b/src/internal/syscall/unix/at_darwin.go
@@ -102,3 +102,29 @@ func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
}
return nil
}
+
+func libc_renameat_trampoline()
+
+//go:cgo_import_dynamic libc_renameat renameat "/usr/lib/libSystem.B.dylib"
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error {
+ oldp, err := syscall.BytePtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ newp, err := syscall.BytePtrFromString(newpath)
+ if err != nil {
+ return err
+ }
+ _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_renameat_trampoline),
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ 0,
+ 0)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/internal/syscall/unix/at_libc.go b/src/internal/syscall/unix/at_libc.go
index d47f69db6f..36a9f22b2a 100644
--- a/src/internal/syscall/unix/at_libc.go
+++ b/src/internal/syscall/unix/at_libc.go
@@ -18,6 +18,7 @@ import (
//go:linkname procMkdirat libc_mkdirat
//go:linkname procFchmodat libc_fchmodat
//go:linkname procFchownat libc_fchownat
+//go:linkname procRenameat libc_renameat
var (
procFstatat,
@@ -26,7 +27,8 @@ var (
procReadlinkat,
procMkdirat,
procFchmodat,
- procFchownat uintptr
+ procFchownat,
+ procRenameat uintptr
)
func Unlinkat(dirfd int, path string, flags int) error {
@@ -160,3 +162,25 @@ func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
}
return nil
}
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error {
+ oldp, err := syscall.BytePtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ newp, err := syscall.BytePtrFromString(newpath)
+ if err != nil {
+ return err
+ }
+ _, _, errno := syscall6(uintptr(unsafe.Pointer(&procRenameat)), 4,
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ 0,
+ 0)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/internal/syscall/unix/at_openbsd.go b/src/internal/syscall/unix/at_openbsd.go
index 22c959b0c7..bd56aac70d 100644
--- a/src/internal/syscall/unix/at_openbsd.go
+++ b/src/internal/syscall/unix/at_openbsd.go
@@ -93,3 +93,29 @@ func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
}
return nil
}
+
+//go:cgo_import_dynamic libc_renameat renameat "libc.so"
+
+func libc_renameat_trampoline()
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error {
+ oldp, err := syscall.BytePtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ newp, err := syscall.BytePtrFromString(newpath)
+ if err != nil {
+ return err
+ }
+ _, _, errno := syscall_syscall6(abi.FuncPCABI0(libc_renameat_trampoline),
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ 0,
+ 0)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/internal/syscall/unix/at_solaris.go b/src/internal/syscall/unix/at_solaris.go
index f84e8e35da..1241827cff 100644
--- a/src/internal/syscall/unix/at_solaris.go
+++ b/src/internal/syscall/unix/at_solaris.go
@@ -17,6 +17,7 @@ func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, e
//go:cgo_import_dynamic libc_fchownat fchownat "libc.so"
//go:cgo_import_dynamic libc_fstatat fstatat "libc.so"
//go:cgo_import_dynamic libc_openat openat "libc.so"
+//go:cgo_import_dynamic libc_renameat renameat "libc.so"
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.so"
//go:cgo_import_dynamic libc_readlinkat readlinkat "libc.so"
//go:cgo_import_dynamic libc_mkdirat mkdirat "libc.so"
diff --git a/src/internal/syscall/unix/at_sysnum_dragonfly.go b/src/internal/syscall/unix/at_sysnum_dragonfly.go
index 1e89e97f38..f4418776cd 100644
--- a/src/internal/syscall/unix/at_sysnum_dragonfly.go
+++ b/src/internal/syscall/unix/at_sysnum_dragonfly.go
@@ -14,6 +14,7 @@ const (
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
+ renameatTrap uintptr = syscall.SYS_RENAMEAT
AT_EACCESS = 0x4
AT_FDCWD = 0xfffafdcd
diff --git a/src/internal/syscall/unix/at_sysnum_freebsd.go b/src/internal/syscall/unix/at_sysnum_freebsd.go
index 59a8c2ce5a..d1bec15343 100644
--- a/src/internal/syscall/unix/at_sysnum_freebsd.go
+++ b/src/internal/syscall/unix/at_sysnum_freebsd.go
@@ -21,4 +21,5 @@ const (
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
+ renameatTrap uintptr = syscall.SYS_RENAMEAT
)
diff --git a/src/internal/syscall/unix/at_sysnum_netbsd.go b/src/internal/syscall/unix/at_sysnum_netbsd.go
index bb946b6581..db42be58b7 100644
--- a/src/internal/syscall/unix/at_sysnum_netbsd.go
+++ b/src/internal/syscall/unix/at_sysnum_netbsd.go
@@ -14,6 +14,7 @@ const (
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
+ renameatTrap uintptr = syscall.SYS_RENAMEAT
)
const (
diff --git a/src/internal/syscall/unix/at_wasip1.go b/src/internal/syscall/unix/at_wasip1.go
index 3fdc95436c..2bd55ca0e7 100644
--- a/src/internal/syscall/unix/at_wasip1.go
+++ b/src/internal/syscall/unix/at_wasip1.go
@@ -111,6 +111,24 @@ func Fchownat(dirfd int, path string, uid, gid int, flags int) error {
return syscall.ENOSYS
}
+//go:wasmimport wasi_snapshot_preview1 path_rename
+//go:noescape
+func path_rename(oldFd int32, oldPath *byte, oldPathLen size, newFd int32, newPath *byte, newPathLen size) syscall.Errno
+
+func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error {
+ if oldpath == "" || newpath == "" {
+ return syscall.EINVAL
+ }
+ return errnoErr(path_rename(
+ int32(olddirfd),
+ unsafe.StringData(oldpath),
+ size(len(oldpath)),
+ int32(newdirfd),
+ unsafe.StringData(newpath),
+ size(len(newpath)),
+ ))
+}
+
//go:wasmimport wasi_snapshot_preview1 path_create_directory
//go:noescape
func path_create_directory(fd int32, path *byte, pathLen size) syscall.Errno
diff --git a/src/internal/syscall/unix/renameat2_sysnum_linux.go b/src/internal/syscall/unix/renameat2_sysnum_linux.go
new file mode 100644
index 0000000000..e41a65db79
--- /dev/null
+++ b/src/internal/syscall/unix/renameat2_sysnum_linux.go
@@ -0,0 +1,16 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && (loong64 || riscv64)
+
+package unix
+
+import "syscall"
+
+const (
+ // loong64 and riscv64 only have renameat2.
+ // renameat2 has an extra flags parameter.
+ // When called with a 0 flags it is identical to renameat.
+ renameatTrap uintptr = syscall.SYS_RENAMEAT2
+)
diff --git a/src/internal/syscall/unix/renameat_sysnum_linux.go b/src/internal/syscall/unix/renameat_sysnum_linux.go
new file mode 100644
index 0000000000..d3663ad1dc
--- /dev/null
+++ b/src/internal/syscall/unix/renameat_sysnum_linux.go
@@ -0,0 +1,13 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && !(loong64 || riscv64)
+
+package unix
+
+import "syscall"
+
+const (
+ renameatTrap uintptr = syscall.SYS_RENAMEAT
+)
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
+}