aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitri Goutnik <dgoutnik@gmail.com>2023-02-23 08:20:32 -0500
committerDmitri Goutnik <dgoutnik@gmail.com>2023-02-24 13:20:54 +0000
commit92c4c39f76b9a9df34c8a64fb56390964eeef31c (patch)
tree9a6f1436c527eb064ccb36ad10276c694d6d8619
parent748af6eb5dfa1396074b92561317f5dede19bcd9 (diff)
downloadgolang-x-sys-92c4c39f76b9a9df34c8a64fb56390964eeef31c.tar.gz
unix: add Dup3 on FreeBSD
Other BSDs provide dup3(2) syscall, on FreeBSD it is implemented as libc function using fcntl(2). This CL adds similar Go implementation. Fixes golang/go#55935 Change-Id: I9c6d762415c7bed5442966a7fcbf9a6f8dfdaf2a Reviewed-on: https://go-review.googlesource.com/c/sys/+/470675 TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Ian Lance Taylor <iant@google.com> Reviewed-by: Than McIntosh <thanm@google.com> Reviewed-by: Tobias Klauser <tobias.klauser@gmail.com> Reviewed-by: Ian Lance Taylor <iant@google.com>
-rw-r--r--unix/dup3_test.go65
-rw-r--r--unix/syscall_freebsd.go12
2 files changed, 77 insertions, 0 deletions
diff --git a/unix/dup3_test.go b/unix/dup3_test.go
new file mode 100644
index 0000000..9d92a6e
--- /dev/null
+++ b/unix/dup3_test.go
@@ -0,0 +1,65 @@
+// Copyright 2023 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 freebsd || linux || netbsd || openbsd
+// +build freebsd linux netbsd openbsd
+
+package unix_test
+
+import (
+ "os"
+ "runtime"
+ "testing"
+
+ "golang.org/x/sys/unix"
+)
+
+func TestDup3(t *testing.T) {
+ tempFile, err := os.CreateTemp("", "TestDup")
+ if err != nil {
+ t.Fatalf("CreateTemp failed: %v", err)
+ }
+ defer os.Remove(tempFile.Name())
+ defer tempFile.Close()
+ oldFd := int(tempFile.Fd())
+
+ // On NetBSD, it is not an error if oldFd == newFd
+ if runtime.GOOS != "netbsd" {
+ if got, want := unix.Dup3(oldFd, oldFd, 0), unix.EINVAL; got != want {
+ t.Fatalf("Dup3: expected err %v, got %v", want, got)
+ }
+ }
+
+ // Create and reserve a file descriptor.
+ // Dup3 automatically closes it before reusing it.
+ nullFile, err := os.Open("/dev/null")
+ if err != nil {
+ t.Fatalf("Open failed: %v", err)
+ }
+ defer nullFile.Close()
+ newFd := int(nullFile.Fd())
+
+ err = unix.Dup3(oldFd, newFd, 0)
+ if err != nil {
+ t.Fatalf("Dup3: %v", err)
+ }
+
+ b1 := []byte("Test123")
+ b2 := make([]byte, 7)
+ _, err = unix.Write(newFd, b1)
+ if err != nil {
+ t.Fatalf("Write to Dup3 fd failed: %v", err)
+ }
+ _, err = unix.Seek(oldFd, 0, 0)
+ if err != nil {
+ t.Fatalf("Seek failed: %v", err)
+ }
+ _, err = unix.Read(oldFd, b2)
+ if err != nil {
+ t.Fatalf("Read back failed: %v", err)
+ }
+ if string(b1) != string(b2) {
+ t.Errorf("Dup3: read %q from file, want %q", string(b2), string(b1))
+ }
+}
diff --git a/unix/syscall_freebsd.go b/unix/syscall_freebsd.go
index 11ab287..5bdde03 100644
--- a/unix/syscall_freebsd.go
+++ b/unix/syscall_freebsd.go
@@ -325,6 +325,18 @@ func PtraceSingleStep(pid int) (err error) {
return ptrace(PT_STEP, pid, 1, 0)
}
+func Dup3(oldfd, newfd, flags int) error {
+ if oldfd == newfd || flags&^O_CLOEXEC != 0 {
+ return EINVAL
+ }
+ how := F_DUP2FD
+ if flags&O_CLOEXEC != 0 {
+ how = F_DUP2FD_CLOEXEC
+ }
+ _, err := fcntl(oldfd, how, newfd)
+ return err
+}
+
/*
* Exposed directly
*/