aboutsummaryrefslogtreecommitdiff
path: root/src/internal
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2025-03-20 12:41:21 -0700
committerGopher Robot <gobot@golang.org>2025-03-24 07:53:38 -0700
commitd2d1fd68b6299d4645298e6d70fe8e8cfd98001a (patch)
tree8108bd9125f95c6a66e5c2c30db5614a6c939ff8 /src/internal
parent4ae6ab2bdfe3ebe8340d0d49fd2bb73f1a3e19ff (diff)
downloadgo-d2d1fd68b6299d4645298e6d70fe8e8cfd98001a.tar.xz
os: add Root.Link
For #67002 Change-Id: I223f3f2dbc8b02726f4ce5a017c628c4a20f109a Reviewed-on: https://go-review.googlesource.com/c/go/+/659757 Reviewed-by: Quim Muntal <quimmuntal@gmail.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
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_aix.go1
-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_linux.go1
-rw-r--r--src/internal/syscall/unix/at_sysnum_netbsd.go1
-rw-r--r--src/internal/syscall/unix/at_wasip1.go19
-rw-r--r--src/internal/syscall/windows/at_windows.go48
-rw-r--r--src/internal/syscall/windows/types_windows.go8
15 files changed, 183 insertions, 1 deletions
diff --git a/src/internal/syscall/unix/asm_darwin.s b/src/internal/syscall/unix/asm_darwin.s
index a72240f512..79d384c941 100644
--- a/src/internal/syscall/unix/asm_darwin.s
+++ b/src/internal/syscall/unix/asm_darwin.s
@@ -28,3 +28,4 @@ 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)
+TEXT ·libc_linkat_trampoline(SB),NOSPLIT,$0-0; JMP libc_linkat(SB)
diff --git a/src/internal/syscall/unix/asm_openbsd.s b/src/internal/syscall/unix/asm_openbsd.s
index 2b88b6988c..481dd7d700 100644
--- a/src/internal/syscall/unix/asm_openbsd.s
+++ b/src/internal/syscall/unix/asm_openbsd.s
@@ -20,3 +20,5 @@ TEXT ·libc_fchownat_trampoline(SB),NOSPLIT,$0-0
JMP libc_fchownat(SB)
TEXT ·libc_renameat_trampoline(SB),NOSPLIT,$0-0
JMP libc_renameat(SB)
+TEXT ·libc_linkat_trampoline(SB),NOSPLIT,$0-0
+ JMP libc_linkat(SB)
diff --git a/src/internal/syscall/unix/at.go b/src/internal/syscall/unix/at.go
index be7920c115..4549a07f8c 100644
--- a/src/internal/syscall/unix/at.go
+++ b/src/internal/syscall/unix/at.go
@@ -136,3 +136,25 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error
}
return nil
}
+
+func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flag int) error {
+ oldp, err := syscall.BytePtrFromString(oldpath)
+ if err != nil {
+ return err
+ }
+ newp, err := syscall.BytePtrFromString(newpath)
+ if err != nil {
+ return err
+ }
+ _, _, errno := syscall.Syscall6(linkatTrap,
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ uintptr(flag),
+ 0)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
diff --git a/src/internal/syscall/unix/at_aix.go b/src/internal/syscall/unix/at_aix.go
index d277cd332f..573554927e 100644
--- a/src/internal/syscall/unix/at_aix.go
+++ b/src/internal/syscall/unix/at_aix.go
@@ -7,6 +7,7 @@ package unix
//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_fchownat fchownat "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_fstatat fstatat "libc.a/shr_64.o"
+//go:cgo_import_dynamic libc_linkat linkat "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_openat openat "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_renameat renameat "libc.a/shr_64.o"
//go:cgo_import_dynamic libc_unlinkat unlinkat "libc.a/shr_64.o"
diff --git a/src/internal/syscall/unix/at_darwin.go b/src/internal/syscall/unix/at_darwin.go
index 4f39b76ad1..61437672ee 100644
--- a/src/internal/syscall/unix/at_darwin.go
+++ b/src/internal/syscall/unix/at_darwin.go
@@ -128,3 +128,29 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error
}
return nil
}
+
+func libc_linkat_trampoline()
+
+//go:cgo_import_dynamic libc_linkat linkat "/usr/lib/libSystem.B.dylib"
+
+func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flag int) 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_linkat_trampoline),
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ uintptr(flag),
+ 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 36a9f22b2a..b32d3bba39 100644
--- a/src/internal/syscall/unix/at_libc.go
+++ b/src/internal/syscall/unix/at_libc.go
@@ -19,6 +19,7 @@ import (
//go:linkname procFchmodat libc_fchmodat
//go:linkname procFchownat libc_fchownat
//go:linkname procRenameat libc_renameat
+//go:linkname procLinkat libc_linkat
var (
procFstatat,
@@ -28,7 +29,8 @@ var (
procMkdirat,
procFchmodat,
procFchownat,
- procRenameat uintptr
+ procRenameat,
+ procLinkat uintptr
)
func Unlinkat(dirfd int, path string, flags int) error {
@@ -184,3 +186,25 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error
}
return nil
}
+
+func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flag int) 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(&procLinkat)), 5,
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ uintptr(flag),
+ 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 bd56aac70d..2a433930f3 100644
--- a/src/internal/syscall/unix/at_openbsd.go
+++ b/src/internal/syscall/unix/at_openbsd.go
@@ -119,3 +119,29 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error
}
return nil
}
+
+func libc_linkat_trampoline()
+
+//go:cgo_import_dynamic libc_linkat linkat "libc.so"
+
+func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flag int) 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_linkat_trampoline),
+ uintptr(olddirfd),
+ uintptr(unsafe.Pointer(oldp)),
+ uintptr(newdirfd),
+ uintptr(unsafe.Pointer(newp)),
+ uintptr(flag),
+ 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 1241827cff..abfda15688 100644
--- a/src/internal/syscall/unix/at_solaris.go
+++ b/src/internal/syscall/unix/at_solaris.go
@@ -16,6 +16,7 @@ func rawSyscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, e
//go:cgo_import_dynamic libc_fchmodat fchmodat "libc.so"
//go:cgo_import_dynamic libc_fchownat fchownat "libc.so"
//go:cgo_import_dynamic libc_fstatat fstatat "libc.so"
+//go:cgo_import_dynamic libc_linkat linkat "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"
diff --git a/src/internal/syscall/unix/at_sysnum_dragonfly.go b/src/internal/syscall/unix/at_sysnum_dragonfly.go
index f4418776cd..3ba2c54152 100644
--- a/src/internal/syscall/unix/at_sysnum_dragonfly.go
+++ b/src/internal/syscall/unix/at_sysnum_dragonfly.go
@@ -15,6 +15,7 @@ const (
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
renameatTrap uintptr = syscall.SYS_RENAMEAT
+ linkatTrap uintptr = syscall.SYS_LINKAT
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 d1bec15343..032b8b5276 100644
--- a/src/internal/syscall/unix/at_sysnum_freebsd.go
+++ b/src/internal/syscall/unix/at_sysnum_freebsd.go
@@ -22,4 +22,5 @@ const (
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
renameatTrap uintptr = syscall.SYS_RENAMEAT
+ linkatTrap uintptr = syscall.SYS_LINKAT
)
diff --git a/src/internal/syscall/unix/at_sysnum_linux.go b/src/internal/syscall/unix/at_sysnum_linux.go
index 35cc4307e9..6b8bebff2a 100644
--- a/src/internal/syscall/unix/at_sysnum_linux.go
+++ b/src/internal/syscall/unix/at_sysnum_linux.go
@@ -13,6 +13,7 @@ const (
mkdiratTrap uintptr = syscall.SYS_MKDIRAT
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
+ linkatTrap uintptr = syscall.SYS_LINKAT
)
const (
diff --git a/src/internal/syscall/unix/at_sysnum_netbsd.go b/src/internal/syscall/unix/at_sysnum_netbsd.go
index db42be58b7..01e10ddd59 100644
--- a/src/internal/syscall/unix/at_sysnum_netbsd.go
+++ b/src/internal/syscall/unix/at_sysnum_netbsd.go
@@ -15,6 +15,7 @@ const (
fchmodatTrap uintptr = syscall.SYS_FCHMODAT
fchownatTrap uintptr = syscall.SYS_FCHOWNAT
renameatTrap uintptr = syscall.SYS_RENAMEAT
+ linkatTrap uintptr = syscall.SYS_LINKAT
)
const (
diff --git a/src/internal/syscall/unix/at_wasip1.go b/src/internal/syscall/unix/at_wasip1.go
index 2bd55ca0e7..72537caf1e 100644
--- a/src/internal/syscall/unix/at_wasip1.go
+++ b/src/internal/syscall/unix/at_wasip1.go
@@ -129,6 +129,25 @@ func Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) error
))
}
+//go:wasmimport wasi_snapshot_preview1 path_link
+//go:noescape
+func path_link(oldFd int32, oldFlags uint32, oldPath *byte, oldPathLen size, newFd int32, newPath *byte, newPathLen size) syscall.Errno
+
+func Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flag int) error {
+ if oldpath == "" || newpath == "" {
+ return syscall.EINVAL
+ }
+ return errnoErr(path_link(
+ int32(olddirfd),
+ 0,
+ 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/windows/at_windows.go b/src/internal/syscall/windows/at_windows.go
index 311e143b9b..4b939d46ab 100644
--- a/src/internal/syscall/windows/at_windows.go
+++ b/src/internal/syscall/windows/at_windows.go
@@ -328,3 +328,51 @@ func Renameat(olddirfd syscall.Handle, oldpath string, newdirfd syscall.Handle,
}
return err
}
+
+func Linkat(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|FILE_WRITE_ATTRIBUTES,
+ 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)
+
+ linkInfo := FILE_LINK_INFORMATION{
+ RootDirectory: newdirfd,
+ }
+ p16, err := syscall.UTF16FromString(newpath)
+ if err != nil {
+ return err
+ }
+ if len(p16) > len(linkInfo.FileName) {
+ return syscall.EINVAL
+ }
+ copy(linkInfo.FileName[:], p16)
+ linkInfo.FileNameLength = uint32((len(p16) - 1) * 2)
+
+ const (
+ FileLinkInformation = 11
+ )
+ err = NtSetInformationFile(
+ h,
+ &IO_STATUS_BLOCK{},
+ uintptr(unsafe.Pointer(&linkInfo)),
+ uint32(unsafe.Sizeof(FILE_LINK_INFORMATION{})),
+ FileLinkInformation,
+ )
+ 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 718a4b863a..adc8b00bd8 100644
--- a/src/internal/syscall/windows/types_windows.go
+++ b/src/internal/syscall/windows/types_windows.go
@@ -238,3 +238,11 @@ type FILE_RENAME_INFORMATION_EX struct {
FileNameLength uint32
FileName [syscall.MAX_PATH]uint16
}
+
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_file_link_information
+type FILE_LINK_INFORMATION struct {
+ ReplaceIfExists bool
+ RootDirectory syscall.Handle
+ FileNameLength uint32
+ FileName [syscall.MAX_PATH]uint16
+}