summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/packetimpact/testbench/connections.go2
-rw-r--r--test/packetimpact/testbench/dut.go16
-rw-r--r--test/packetimpact/tests/BUILD14
-rw-r--r--test/packetimpact/tests/tcp_close_wait_ack_test.go109
-rw-r--r--test/packetimpact/tests/tcp_linger_test.go253
-rw-r--r--test/packetimpact/tests/tcp_unacc_seq_ack_test.go234
-rw-r--r--test/perf/linux/BUILD16
-rw-r--r--test/perf/linux/open_read_close_benchmark.cc61
-rw-r--r--test/syscalls/linux/open_create.cc116
-rw-r--r--test/syscalls/linux/pty.cc243
-rw-r--r--test/syscalls/linux/pty_root.cc4
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc3
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.cc119
-rw-r--r--test/syscalls/linux/socket_ip_udp_generic.cc30
-rw-r--r--test/syscalls/linux/statfs.cc3
-rw-r--r--test/util/pty_util.cc10
-rw-r--r--test/util/pty_util.h8
17 files changed, 564 insertions, 677 deletions
diff --git a/test/packetimpact/testbench/connections.go b/test/packetimpact/testbench/connections.go
index 3af5f83fd..a90046f69 100644
--- a/test/packetimpact/testbench/connections.go
+++ b/test/packetimpact/testbench/connections.go
@@ -615,7 +615,7 @@ func (conn *Connection) ExpectFrame(t *testing.T, layers Layers, timeout time.Du
if errs == nil {
return nil, fmt.Errorf("got no frames matching %v during %s", layers, timeout)
}
- return nil, fmt.Errorf("got no frames matching %v during %s: got %w", layers, timeout, errs)
+ return nil, fmt.Errorf("got frames %w want %v during %s", errs, layers, timeout)
}
if conn.match(layers, gotLayers) {
for i, s := range conn.layerStates {
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index 6165ab293..ff269d949 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -16,13 +16,11 @@ package testbench
import (
"context"
- "encoding/binary"
"flag"
"net"
"strconv"
"syscall"
"testing"
- "time"
pb "gvisor.dev/gvisor/test/packetimpact/proto/posix_server_go_proto"
@@ -703,20 +701,6 @@ func (dut *DUT) RecvWithErrno(ctx context.Context, t *testing.T, sockfd, len, fl
return resp.GetRet(), resp.GetBuf(), syscall.Errno(resp.GetErrno_())
}
-// SetSockLingerOption sets SO_LINGER socket option on the DUT.
-func (dut *DUT) SetSockLingerOption(t *testing.T, sockfd int32, timeout time.Duration, enable bool) {
- var linger unix.Linger
- if enable {
- linger.Onoff = 1
- }
- linger.Linger = int32(timeout / time.Second)
-
- buf := make([]byte, 8)
- binary.LittleEndian.PutUint32(buf, uint32(linger.Onoff))
- binary.LittleEndian.PutUint32(buf[4:], uint32(linger.Linger))
- dut.SetSockOpt(t, sockfd, unix.SOL_SOCKET, unix.SO_LINGER, buf)
-}
-
// Shutdown calls shutdown on the DUT and causes a fatal test failure if it doesn't
// succeed. If more control over the timeout or error handling is needed, use
// ShutdownWithErrno.
diff --git a/test/packetimpact/tests/BUILD b/test/packetimpact/tests/BUILD
index 7a7152fa5..e1ed0cc60 100644
--- a/test/packetimpact/tests/BUILD
+++ b/test/packetimpact/tests/BUILD
@@ -166,8 +166,8 @@ packetimpact_go_test(
)
packetimpact_go_test(
- name = "tcp_close_wait_ack",
- srcs = ["tcp_close_wait_ack_test.go"],
+ name = "tcp_unacc_seq_ack",
+ srcs = ["tcp_unacc_seq_ack_test.go"],
deps = [
"//pkg/tcpip/header",
"//pkg/tcpip/seqnum",
@@ -308,13 +308,3 @@ packetimpact_go_test(
"@org_golang_x_sys//unix:go_default_library",
],
)
-
-packetimpact_go_test(
- name = "tcp_linger",
- srcs = ["tcp_linger_test.go"],
- deps = [
- "//pkg/tcpip/header",
- "//test/packetimpact/testbench",
- "@org_golang_x_sys//unix:go_default_library",
- ],
-)
diff --git a/test/packetimpact/tests/tcp_close_wait_ack_test.go b/test/packetimpact/tests/tcp_close_wait_ack_test.go
deleted file mode 100644
index e6a96f214..000000000
--- a/test/packetimpact/tests/tcp_close_wait_ack_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_close_wait_ack_test
-
-import (
- "flag"
- "fmt"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/pkg/tcpip/seqnum"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func TestCloseWaitAck(t *testing.T) {
- for _, tt := range []struct {
- description string
- makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
- seqNumOffset seqnum.Size
- expectAck bool
- }{
- {"OTW", generateOTWSeqSegment, 0, false},
- {"OTW", generateOTWSeqSegment, 1, true},
- {"OTW", generateOTWSeqSegment, 2, true},
- {"ACK", generateUnaccACKSegment, 0, false},
- {"ACK", generateUnaccACKSegment, 1, true},
- {"ACK", generateUnaccACKSegment, 2, true},
- } {
- t.Run(fmt.Sprintf("%s%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
- dut := testbench.NewDUT(t)
- defer dut.TearDown()
- listenFd, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- defer dut.Close(t, listenFd)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- defer conn.Close(t)
-
- conn.Connect(t)
- acceptFd, _ := dut.Accept(t, listenFd)
-
- // Send a FIN to DUT to intiate the active close
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
- gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
- if err != nil {
- t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
- }
- windowSize := seqnum.Size(*gotTCP.WindowSize)
-
- // Send a segment with OTW Seq / unacc ACK and expect an ACK back
- conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), &testbench.Payload{Bytes: []byte("Sample Data")})
- gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
- if tt.expectAck && err != nil {
- t.Fatalf("expected an ack but got none: %s", err)
- }
- if !tt.expectAck && gotAck != nil {
- t.Fatalf("expected no ack but got one: %s", gotAck)
- }
-
- // Now let's verify DUT is indeed in CLOSE_WAIT
- dut.Close(t, acceptFd)
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
- t.Fatalf("expected DUT to send a FIN: %s", err)
- }
- // Ack the FIN from DUT
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- // Send some extra data to DUT
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, &testbench.Payload{Bytes: []byte("Sample Data")})
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
- t.Fatalf("expected DUT to send an RST: %s", err)
- }
- })
- }
-}
-
-// generateOTWSeqSegment generates an segment with
-// seqnum = RCV.NXT + RCV.WND + seqNumOffset, the generated segment is only
-// acceptable when seqNumOffset is 0, otherwise an ACK is expected from the
-// receiver.
-func generateOTWSeqSegment(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
- lastAcceptable := conn.LocalSeqNum(t).Add(windowSize)
- otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
- return testbench.TCP{SeqNum: testbench.Uint32(otwSeq), Flags: testbench.Uint8(header.TCPFlagAck)}
-}
-
-// generateUnaccACKSegment generates an segment with
-// acknum = SND.NXT + seqNumOffset, the generated segment is only acceptable
-// when seqNumOffset is 0, otherwise an ACK is expected from the receiver.
-func generateUnaccACKSegment(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
- lastAcceptable := conn.RemoteSeqNum(t)
- unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
- return testbench.TCP{AckNum: testbench.Uint32(unaccAck), Flags: testbench.Uint8(header.TCPFlagAck)}
-}
diff --git a/test/packetimpact/tests/tcp_linger_test.go b/test/packetimpact/tests/tcp_linger_test.go
deleted file mode 100644
index 913e49e06..000000000
--- a/test/packetimpact/tests/tcp_linger_test.go
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2020 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package tcp_linger_test
-
-import (
- "context"
- "flag"
- "syscall"
- "testing"
- "time"
-
- "golang.org/x/sys/unix"
- "gvisor.dev/gvisor/pkg/tcpip/header"
- "gvisor.dev/gvisor/test/packetimpact/testbench"
-)
-
-func init() {
- testbench.RegisterFlags(flag.CommandLine)
-}
-
-func createSocket(t *testing.T, dut testbench.DUT) (int32, int32, testbench.TCPIPv4) {
- listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
- conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
- conn.Connect(t)
- acceptFD, _ := dut.Accept(t, listenFD)
- return acceptFD, listenFD, conn
-}
-
-func closeAll(t *testing.T, dut testbench.DUT, listenFD int32, conn testbench.TCPIPv4) {
- conn.Close(t)
- dut.Close(t, listenFD)
- dut.TearDown()
-}
-
-// lingerDuration is the timeout value used with SO_LINGER socket option.
-const lingerDuration = 3 * time.Second
-
-// TestTCPLingerZeroTimeout tests when SO_LINGER is set with zero timeout. DUT
-// should send RST-ACK when socket is closed.
-func TestTCPLingerZeroTimeout(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- acceptFD, listenFD, conn := createSocket(t, dut)
- defer closeAll(t, dut, listenFD, conn)
-
- dut.SetSockLingerOption(t, acceptFD, 0, true)
- dut.Close(t, acceptFD)
-
- // If the linger timeout is set to zero, the DUT should send a RST.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected RST-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-}
-
-// TestTCPLingerOff tests when SO_LINGER is not set. DUT should send FIN-ACK
-// when socket is closed.
-func TestTCPLingerOff(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- acceptFD, listenFD, conn := createSocket(t, dut)
- defer closeAll(t, dut, listenFD, conn)
-
- dut.Close(t, acceptFD)
-
- // If SO_LINGER is not set, DUT should send a FIN-ACK.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-}
-
-// TestTCPLingerNonZeroTimeout tests when SO_LINGER is set with non-zero timeout.
-// DUT should close the socket after timeout.
-func TestTCPLingerNonZeroTimeout(t *testing.T) {
- for _, tt := range []struct {
- description string
- lingerOn bool
- }{
- {"WithNonZeroLinger", true},
- {"WithoutLinger", false},
- } {
- t.Run(tt.description, func(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- acceptFD, listenFD, conn := createSocket(t, dut)
- defer closeAll(t, dut, listenFD, conn)
-
- dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn)
-
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
- start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
- }
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- })
- }
-}
-
-// TestTCPLingerSendNonZeroTimeout tests when SO_LINGER is set with non-zero
-// timeout and send a packet. DUT should close the socket after timeout.
-func TestTCPLingerSendNonZeroTimeout(t *testing.T) {
- for _, tt := range []struct {
- description string
- lingerOn bool
- }{
- {"WithSendNonZeroLinger", true},
- {"WithoutLinger", false},
- } {
- t.Run(tt.description, func(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- acceptFD, listenFD, conn := createSocket(t, dut)
- defer closeAll(t, dut, listenFD, conn)
-
- dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn)
-
- // Send data.
- sampleData := []byte("Sample Data")
- dut.Send(t, acceptFD, sampleData, 0)
-
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
- start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
- }
-
- samplePayload := &testbench.Payload{Bytes: sampleData}
- if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
- }
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- })
- }
-}
-
-// TestTCPLingerShutdownZeroTimeout tests SO_LINGER with shutdown() and zero
-// timeout. DUT should send RST-ACK when socket is closed.
-func TestTCPLingerShutdownZeroTimeout(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- acceptFD, listenFD, conn := createSocket(t, dut)
- defer closeAll(t, dut, listenFD, conn)
-
- dut.SetSockLingerOption(t, acceptFD, 0, true)
- dut.Shutdown(t, acceptFD, syscall.SHUT_RDWR)
- dut.Close(t, acceptFD)
-
- // Shutdown will send FIN-ACK with read/write option.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
-
- // If the linger timeout is set to zero, the DUT should send a RST.
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected RST-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
-}
-
-// TestTCPLingerShutdownSendNonZeroTimeout tests SO_LINGER with shutdown() and
-// non-zero timeout. DUT should close the socket after timeout.
-func TestTCPLingerShutdownSendNonZeroTimeout(t *testing.T) {
- for _, tt := range []struct {
- description string
- lingerOn bool
- }{
- {"shutdownRDWR", true},
- {"shutdownRDWR", false},
- } {
- t.Run(tt.description, func(t *testing.T) {
- // Create a socket, listen, TCP connect, and accept.
- dut := testbench.NewDUT(t)
- acceptFD, listenFD, conn := createSocket(t, dut)
- defer closeAll(t, dut, listenFD, conn)
-
- dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn)
-
- // Send data.
- sampleData := []byte("Sample Data")
- dut.Send(t, acceptFD, sampleData, 0)
-
- dut.Shutdown(t, acceptFD, syscall.SHUT_RDWR)
-
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
- start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
- }
-
- samplePayload := &testbench.Payload{Bytes: sampleData}
- if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
- }
-
- if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
- t.Errorf("expected FIN-ACK packet within a second but got none: %s", err)
- }
- conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
- })
- }
-}
diff --git a/test/packetimpact/tests/tcp_unacc_seq_ack_test.go b/test/packetimpact/tests/tcp_unacc_seq_ack_test.go
new file mode 100644
index 000000000..d078bbf15
--- /dev/null
+++ b/test/packetimpact/tests/tcp_unacc_seq_ack_test.go
@@ -0,0 +1,234 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package tcp_unacc_seq_ack_test
+
+import (
+ "flag"
+ "fmt"
+ "syscall"
+ "testing"
+ "time"
+
+ "golang.org/x/sys/unix"
+ "gvisor.dev/gvisor/pkg/tcpip/header"
+ "gvisor.dev/gvisor/pkg/tcpip/seqnum"
+ "gvisor.dev/gvisor/test/packetimpact/testbench"
+)
+
+func init() {
+ testbench.RegisterFlags(flag.CommandLine)
+}
+
+func TestEstablishedUnaccSeqAck(t *testing.T) {
+ for _, tt := range []struct {
+ description string
+ makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
+ seqNumOffset seqnum.Size
+ expectAck bool
+ restoreSeq bool
+ }{
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 0, expectAck: true, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 1, expectAck: true, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 2, expectAck: true, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 0, expectAck: true, restoreSeq: false},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 1, expectAck: false, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 2, expectAck: false, restoreSeq: true},
+ } {
+ t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ defer dut.TearDown()
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ defer dut.Close(t, listenFD)
+ conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+
+ conn.Connect(t)
+ dut.Accept(t, listenFD)
+
+ sampleData := []byte("Sample Data")
+ samplePayload := &testbench.Payload{Bytes: sampleData}
+
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagPsh)}, samplePayload)
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ if err != nil {
+ t.Fatalf("expected ack %s", err)
+ }
+ windowSize := seqnum.Size(*gotTCP.WindowSize)
+
+ origSeq := *conn.LocalSeqNum(t)
+ // Send a segment with OTW Seq / unacc ACK.
+ conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), samplePayload)
+ if tt.restoreSeq {
+ // Restore the local sequence number to ensure that the incoming
+ // ACK matches the TCP layer state.
+ *conn.LocalSeqNum(t) = origSeq
+ }
+ gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ if tt.expectAck && err != nil {
+ t.Fatalf("expected an ack but got none: %s", err)
+ }
+ if err == nil && !tt.expectAck && gotAck != nil {
+ t.Fatalf("expected no ack but got one: %s", gotAck)
+ }
+ })
+ }
+}
+
+func TestPassiveCloseUnaccSeqAck(t *testing.T) {
+ for _, tt := range []struct {
+ description string
+ makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
+ seqNumOffset seqnum.Size
+ expectAck bool
+ }{
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 0, expectAck: false},
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 1, expectAck: true},
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 2, expectAck: true},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 0, expectAck: false},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 1, expectAck: true},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 2, expectAck: true},
+ } {
+ t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ defer dut.TearDown()
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ defer dut.Close(t, listenFD)
+ conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+
+ conn.Connect(t)
+ acceptFD, _ := dut.Accept(t, listenFD)
+
+ // Send a FIN to DUT to intiate the passive close.
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)})
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ if err != nil {
+ t.Fatalf("expected an ACK for our fin and DUT should enter CLOSE_WAIT: %s", err)
+ }
+ windowSize := seqnum.Size(*gotTCP.WindowSize)
+
+ sampleData := []byte("Sample Data")
+ samplePayload := &testbench.Payload{Bytes: sampleData}
+
+ // Send a segment with OTW Seq / unacc ACK.
+ conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, windowSize), samplePayload)
+ gotAck, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second)
+ if tt.expectAck && err != nil {
+ t.Errorf("expected an ack but got none: %s", err)
+ }
+ if err == nil && !tt.expectAck && gotAck != nil {
+ t.Errorf("expected no ack but got one: %s", gotAck)
+ }
+
+ // Now let's verify DUT is indeed in CLOSE_WAIT
+ dut.Close(t, acceptFD)
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck | header.TCPFlagFin)}, time.Second); err != nil {
+ t.Fatalf("expected DUT to send a FIN: %s", err)
+ }
+ // Ack the FIN from DUT
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+ // Send some extra data to DUT
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, samplePayload)
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagRst)}, time.Second); err != nil {
+ t.Fatalf("expected DUT to send an RST: %s", err)
+ }
+ })
+ }
+}
+
+func TestActiveCloseUnaccpSeqAck(t *testing.T) {
+ for _, tt := range []struct {
+ description string
+ makeTestingTCP func(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset, windowSize seqnum.Size) testbench.TCP
+ seqNumOffset seqnum.Size
+ restoreSeq bool
+ }{
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 0, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 1, restoreSeq: true},
+ {description: "OTWSeq", makeTestingTCP: generateOTWSeqSegment, seqNumOffset: 2, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 0, restoreSeq: false},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 1, restoreSeq: true},
+ {description: "UnaccAck", makeTestingTCP: generateUnaccACKSegment, seqNumOffset: 2, restoreSeq: true},
+ } {
+ t.Run(fmt.Sprintf("%s:offset=%d", tt.description, tt.seqNumOffset), func(t *testing.T) {
+ dut := testbench.NewDUT(t)
+ defer dut.TearDown()
+ listenFD, remotePort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1 /*backlog*/)
+ defer dut.Close(t, listenFD)
+ conn := testbench.NewTCPIPv4(t, testbench.TCP{DstPort: &remotePort}, testbench.TCP{SrcPort: &remotePort})
+ defer conn.Close(t)
+
+ conn.Connect(t)
+ acceptFD, _ := dut.Accept(t, listenFD)
+
+ // Trigger active close.
+ dut.Shutdown(t, acceptFD, syscall.SHUT_WR)
+
+ // Get to FIN_WAIT2
+ gotTCP, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)}, time.Second)
+ if err != nil {
+ t.Fatalf("expected a FIN: %s", err)
+ }
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)})
+
+ sendUnaccSeqAck := func(state string) {
+ t.Helper()
+ sampleData := []byte("Sample Data")
+ samplePayload := &testbench.Payload{Bytes: sampleData}
+
+ origSeq := *conn.LocalSeqNum(t)
+ // Send a segment with OTW Seq / unacc ACK.
+ conn.Send(t, tt.makeTestingTCP(t, &conn, tt.seqNumOffset, seqnum.Size(*gotTCP.WindowSize)), samplePayload)
+ if tt.restoreSeq {
+ // Restore the local sequence number to ensure that the
+ // incoming ACK matches the TCP layer state.
+ *conn.LocalSeqNum(t) = origSeq
+ }
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Errorf("expected an ack in %s state, but got none: %s", state, err)
+ }
+ }
+
+ sendUnaccSeqAck("FIN_WAIT2")
+
+ // Send a FIN to DUT to get to TIME_WAIT
+ conn.Send(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagFin | header.TCPFlagAck)})
+ if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.Uint8(header.TCPFlagAck)}, time.Second); err != nil {
+ t.Fatalf("expected an ACK for our fin and DUT should enter TIME_WAIT: %s", err)
+ }
+
+ sendUnaccSeqAck("TIME_WAIT")
+ })
+ }
+}
+
+// generateOTWSeqSegment generates an segment with
+// seqnum = RCV.NXT + RCV.WND + seqNumOffset, the generated segment is only
+// acceptable when seqNumOffset is 0, otherwise an ACK is expected from the
+// receiver.
+func generateOTWSeqSegment(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
+ lastAcceptable := conn.LocalSeqNum(t).Add(windowSize)
+ otwSeq := uint32(lastAcceptable.Add(seqNumOffset))
+ return testbench.TCP{SeqNum: testbench.Uint32(otwSeq), Flags: testbench.Uint8(header.TCPFlagAck)}
+}
+
+// generateUnaccACKSegment generates an segment with
+// acknum = SND.NXT + seqNumOffset, the generated segment is only acceptable
+// when seqNumOffset is 0, otherwise an ACK is expected from the receiver.
+func generateUnaccACKSegment(t *testing.T, conn *testbench.TCPIPv4, seqNumOffset seqnum.Size, windowSize seqnum.Size) testbench.TCP {
+ lastAcceptable := conn.RemoteSeqNum(t)
+ unaccAck := uint32(lastAcceptable.Add(seqNumOffset))
+ return testbench.TCP{AckNum: testbench.Uint32(unaccAck), Flags: testbench.Uint8(header.TCPFlagAck)}
+}
diff --git a/test/perf/linux/BUILD b/test/perf/linux/BUILD
index b4e907826..dd1d2438c 100644
--- a/test/perf/linux/BUILD
+++ b/test/perf/linux/BUILD
@@ -354,3 +354,19 @@ cc_binary(
"//test/util:test_util",
],
)
+
+cc_binary(
+ name = "open_read_close_benchmark",
+ testonly = 1,
+ srcs = [
+ "open_read_close_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ ],
+)
diff --git a/test/perf/linux/open_read_close_benchmark.cc b/test/perf/linux/open_read_close_benchmark.cc
new file mode 100644
index 000000000..8b023a3d8
--- /dev/null
+++ b/test/perf/linux/open_read_close_benchmark.cc
@@ -0,0 +1,61 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
+#include "test/util/fs_util.h"
+#include "test/util/logging.h"
+#include "test/util/temp_path.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+void BM_OpenReadClose(benchmark::State& state) {
+ const int size = state.range(0);
+ std::vector<TempPath> cache;
+ for (int i = 0; i < size; i++) {
+ auto path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ GetAbsoluteTestTmpdir(), "some content", 0644));
+ cache.emplace_back(std::move(path));
+ }
+
+ char buf[1];
+ unsigned int seed = 1;
+ for (auto _ : state) {
+ const int chosen = rand_r(&seed) % size;
+ int fd = open(cache[chosen].path().c_str(), O_RDONLY);
+ TEST_CHECK(fd != -1);
+ TEST_CHECK(read(fd, buf, 1) == 1);
+ close(fd);
+ }
+}
+
+// Gofer dentry cache is 1000 by default. Go over it to force files to be closed
+// for real.
+BENCHMARK(BM_OpenReadClose)->Range(1000, 16384)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/open_create.cc b/test/syscalls/linux/open_create.cc
index 51eacf3f2..78c36f98f 100644
--- a/test/syscalls/linux/open_create.cc
+++ b/test/syscalls/linux/open_create.cc
@@ -88,21 +88,21 @@ TEST(CreateTest, CreateExclusively) {
SyscallFailsWithErrno(EEXIST));
}
-TEST(CreateTeast, CreatWithOTrunc) {
+TEST(CreateTest, CreatWithOTrunc) {
std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC, 0666),
SyscallFailsWithErrno(EISDIR));
}
-TEST(CreateTeast, CreatDirWithOTruncAndReadOnly) {
+TEST(CreateTest, CreatDirWithOTruncAndReadOnly) {
std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncd");
ASSERT_THAT(mkdir(dirpath.c_str(), 0777), SyscallSucceeds());
ASSERT_THAT(open(dirpath.c_str(), O_CREAT | O_TRUNC | O_RDONLY, 0666),
SyscallFailsWithErrno(EISDIR));
}
-TEST(CreateTeast, CreatFileWithOTruncAndReadOnly) {
+TEST(CreateTest, CreatFileWithOTruncAndReadOnly) {
std::string dirpath = JoinPath(GetAbsoluteTestTmpdir(), "truncfile");
int dirfd;
ASSERT_THAT(dirfd = open(dirpath.c_str(), O_RDWR | O_CREAT, 0666),
@@ -149,6 +149,116 @@ TEST(CreateTest, OpenCreateROThenRW) {
EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1));
}
+TEST(CreateTest, ChmodReadToWriteBetweenOpens_NoRandomSave) {
+ // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
+ // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be
+ // cleared for the same reason.
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
+
+ const TempPath file =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400));
+
+ const FileDescriptor rfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
+
+ // Cannot restore after making permissions more restrictive.
+ const DisableSave ds;
+ ASSERT_THAT(fchmod(rfd.get(), 0200), SyscallSucceeds());
+
+ EXPECT_THAT(open(file.path().c_str(), O_RDONLY),
+ SyscallFailsWithErrno(EACCES));
+
+ const FileDescriptor wfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));
+
+ char c = 'x';
+ EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ c = 0;
+ EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ EXPECT_EQ(c, 'x');
+}
+
+TEST(CreateTest, ChmodWriteToReadBetweenOpens_NoRandomSave) {
+ // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
+ // override file read/write permissions.
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
+
+ const TempPath file =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0200));
+
+ const FileDescriptor wfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));
+
+ // Cannot restore after making permissions more restrictive.
+ const DisableSave ds;
+ ASSERT_THAT(fchmod(wfd.get(), 0400), SyscallSucceeds());
+
+ EXPECT_THAT(open(file.path().c_str(), O_WRONLY),
+ SyscallFailsWithErrno(EACCES));
+
+ const FileDescriptor rfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
+
+ char c = 'x';
+ EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ c = 0;
+ EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ EXPECT_EQ(c, 'x');
+}
+
+TEST(CreateTest, CreateWithReadFlagNotAllowedByMode_NoRandomSave) {
+ // The only time we can open a file with flags forbidden by its permissions
+ // is when we are creating the file. We cannot re-open with the same flags,
+ // so we cannot restore an fd obtained from such an operation.
+ const DisableSave ds;
+
+ // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
+ // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be
+ // cleared for the same reason.
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false));
+
+ // Create and open a file with read flag but without read permissions.
+ const std::string path = NewTempAbsPath();
+ const FileDescriptor rfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_RDONLY, 0222));
+
+ EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES));
+ const FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY));
+
+ char c = 'x';
+ EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ c = 0;
+ EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ EXPECT_EQ(c, 'x');
+}
+
+TEST(CreateTest, CreateWithWriteFlagNotAllowedByMode_NoRandomSave) {
+ // The only time we can open a file with flags forbidden by its permissions
+ // is when we are creating the file. We cannot re-open with the same flags,
+ // so we cannot restore an fd obtained from such an operation.
+ const DisableSave ds;
+
+ // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to
+ // override file read/write permissions.
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
+
+ // Create and open a file with write flag but without write permissions.
+ const std::string path = NewTempAbsPath();
+ const FileDescriptor wfd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_WRONLY, 0444));
+
+ EXPECT_THAT(open(path.c_str(), O_WRONLY), SyscallFailsWithErrno(EACCES));
+ const FileDescriptor rfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDONLY));
+
+ char c = 'x';
+ EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ c = 0;
+ EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1));
+ EXPECT_EQ(c, 'x');
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc
index 2e4ab6ca8..0b174e2be 100644
--- a/test/syscalls/linux/pty.cc
+++ b/test/syscalls/linux/pty.cc
@@ -71,7 +71,7 @@ constexpr absl::Duration kTimeout = absl::Seconds(20);
// The maximum line size in bytes returned per read from a pty file.
constexpr int kMaxLineSize = 4096;
-constexpr char kMainPath[] = "/dev/ptmx";
+constexpr char kMasterPath[] = "/dev/ptmx";
// glibc defines its own, different, version of struct termios. We care about
// what the kernel does, not glibc.
@@ -388,22 +388,22 @@ PosixErrorOr<size_t> PollAndReadFd(int fd, void* buf, size_t count,
TEST(PtyTrunc, Truncate) {
// Opening PTYs with O_TRUNC shouldn't cause an error, but calls to
// (f)truncate should.
- FileDescriptor main =
- ASSERT_NO_ERRNO_AND_VALUE(Open(kMainPath, O_RDWR | O_TRUNC));
- int n = ASSERT_NO_ERRNO_AND_VALUE(ReplicaID(main));
+ FileDescriptor master =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(kMasterPath, O_RDWR | O_TRUNC));
+ int n = ASSERT_NO_ERRNO_AND_VALUE(ReplicaID(master));
std::string spath = absl::StrCat("/dev/pts/", n);
FileDescriptor replica =
ASSERT_NO_ERRNO_AND_VALUE(Open(spath, O_RDWR | O_NONBLOCK | O_TRUNC));
- EXPECT_THAT(truncate(kMainPath, 0), SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(truncate(kMasterPath, 0), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(truncate(spath.c_str(), 0), SyscallFailsWithErrno(EINVAL));
- EXPECT_THAT(ftruncate(main.get(), 0), SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(ftruncate(master.get(), 0), SyscallFailsWithErrno(EINVAL));
EXPECT_THAT(ftruncate(replica.get(), 0), SyscallFailsWithErrno(EINVAL));
}
-TEST(BasicPtyTest, StatUnopenedMain) {
+TEST(BasicPtyTest, StatUnopenedMaster) {
struct stat s;
- ASSERT_THAT(stat(kMainPath, &s), SyscallSucceeds());
+ ASSERT_THAT(stat(kMasterPath, &s), SyscallSucceeds());
EXPECT_EQ(s.st_rdev, makedev(TTYAUX_MAJOR, kPtmxMinor));
EXPECT_EQ(s.st_size, 0);
@@ -454,41 +454,41 @@ void ExpectReadable(const FileDescriptor& fd, int expected, char* buf) {
EXPECT_EQ(expected, n);
}
-TEST(BasicPtyTest, OpenMainReplica) {
- FileDescriptor main = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
- FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main));
+TEST(BasicPtyTest, OpenMasterReplica) {
+ FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
+ FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master));
}
-// The replica entry in /dev/pts/ disappears when the main is closed, even if
+// The replica entry in /dev/pts/ disappears when the master is closed, even if
// the replica is still open.
-TEST(BasicPtyTest, ReplicaEntryGoneAfterMainClose) {
- FileDescriptor main = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
- FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main));
+TEST(BasicPtyTest, ReplicaEntryGoneAfterMasterClose) {
+ FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
+ FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master));
// Get pty index.
int index = -1;
- ASSERT_THAT(ioctl(main.get(), TIOCGPTN, &index), SyscallSucceeds());
+ ASSERT_THAT(ioctl(master.get(), TIOCGPTN, &index), SyscallSucceeds());
std::string path = absl::StrCat("/dev/pts/", index);
struct stat st;
EXPECT_THAT(stat(path.c_str(), &st), SyscallSucceeds());
- main.reset();
+ master.reset();
EXPECT_THAT(stat(path.c_str(), &st), SyscallFailsWithErrno(ENOENT));
}
TEST(BasicPtyTest, Getdents) {
- FileDescriptor main1 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
+ FileDescriptor master1 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
int index1 = -1;
- ASSERT_THAT(ioctl(main1.get(), TIOCGPTN, &index1), SyscallSucceeds());
- FileDescriptor replica1 = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main1));
+ ASSERT_THAT(ioctl(master1.get(), TIOCGPTN, &index1), SyscallSucceeds());
+ FileDescriptor replica1 = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master1));
- FileDescriptor main2 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
+ FileDescriptor master2 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR));
int index2 = -1;
- ASSERT_THAT(ioctl(main2.get(), TIOCGPTN, &index2), SyscallSucceeds());
- FileDescriptor replica2 = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main2));
+ ASSERT_THAT(ioctl(master2.get(), TIOCGPTN, &index2), SyscallSucceeds());
+ FileDescriptor replica2 = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master2));
// The directory contains ptmx, index1, and index2. (Plus any additional PTYs
// unrelated to this test.)
@@ -498,9 +498,9 @@ TEST(BasicPtyTest, Getdents) {
EXPECT_THAT(contents, Contains(absl::StrCat(index1)));
EXPECT_THAT(contents, Contains(absl::StrCat(index2)));
- main2.reset();
+ master2.reset();
- // The directory contains ptmx and index1, but not index2 since the main is
+ // The directory contains ptmx and index1, but not index2 since the master is
// closed. (Plus any additional PTYs unrelated to this test.)
contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev/pts/", true));
@@ -519,8 +519,8 @@ TEST(BasicPtyTest, Getdents) {
class PtyTest : public ::testing::Test {
protected:
void SetUp() override {
- main_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK));
- replica_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main_));
+ master_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK));
+ replica_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master_));
}
void DisableCanonical() {
@@ -537,21 +537,22 @@ class PtyTest : public ::testing::Test {
EXPECT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
}
- // Main and replica ends of the PTY. Non-blocking.
- FileDescriptor main_;
+ // Master and replica ends of the PTY. Non-blocking.
+ FileDescriptor master_;
FileDescriptor replica_;
};
-// Main to replica sanity test.
-TEST_F(PtyTest, WriteMainToReplica) {
- // N.B. by default, the replica reads nothing until the main writes a newline.
+// Master to replica sanity test.
+TEST_F(PtyTest, WriteMasterToReplica) {
+ // N.B. by default, the replica reads nothing until the master writes a
+ // newline.
constexpr char kBuf[] = "hello\n";
- EXPECT_THAT(WriteFd(main_.get(), kBuf, sizeof(kBuf) - 1),
+ EXPECT_THAT(WriteFd(master_.get(), kBuf, sizeof(kBuf) - 1),
SyscallSucceedsWithValue(sizeof(kBuf) - 1));
- // Linux moves data from the main to the replica via async work scheduled via
- // tty_flip_buffer_push. Since it is asynchronous, the data may not be
+ // Linux moves data from the master to the replica via async work scheduled
+ // via tty_flip_buffer_push. Since it is asynchronous, the data may not be
// available for reading immediately. Instead we must poll and assert that it
// becomes available "soon".
@@ -561,63 +562,63 @@ TEST_F(PtyTest, WriteMainToReplica) {
EXPECT_EQ(memcmp(buf, kBuf, sizeof(kBuf)), 0);
}
-// Replica to main sanity test.
-TEST_F(PtyTest, WriteReplicaToMain) {
- // N.B. by default, the main reads nothing until the replica writes a newline,
- // and the main gets a carriage return.
+// Replica to master sanity test.
+TEST_F(PtyTest, WriteReplicaToMaster) {
+ // N.B. by default, the master reads nothing until the replica writes a
+ // newline, and the master gets a carriage return.
constexpr char kInput[] = "hello\n";
constexpr char kExpected[] = "hello\r\n";
EXPECT_THAT(WriteFd(replica_.get(), kInput, sizeof(kInput) - 1),
SyscallSucceedsWithValue(sizeof(kInput) - 1));
- // Linux moves data from the main to the replica via async work scheduled via
- // tty_flip_buffer_push. Since it is asynchronous, the data may not be
+ // Linux moves data from the master to the replica via async work scheduled
+ // via tty_flip_buffer_push. Since it is asynchronous, the data may not be
// available for reading immediately. Instead we must poll and assert that it
// becomes available "soon".
char buf[sizeof(kExpected)] = {};
- ExpectReadable(main_, sizeof(buf) - 1, buf);
+ ExpectReadable(master_, sizeof(buf) - 1, buf);
EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0);
}
TEST_F(PtyTest, WriteInvalidUTF8) {
char c = 0xff;
- ASSERT_THAT(syscall(__NR_write, main_.get(), &c, sizeof(c)),
+ ASSERT_THAT(syscall(__NR_write, master_.get(), &c, sizeof(c)),
SyscallSucceedsWithValue(sizeof(c)));
}
-// Both the main and replica report the standard default termios settings.
+// Both the master and replica report the standard default termios settings.
//
-// Note that TCGETS on the main actually redirects to the replica (see comment
-// on MainTermiosUnchangable).
+// Note that TCGETS on the master actually redirects to the replica (see comment
+// on MasterTermiosUnchangable).
TEST_F(PtyTest, DefaultTermios) {
struct kernel_termios t = {};
EXPECT_THAT(ioctl(replica_.get(), TCGETS, &t), SyscallSucceeds());
EXPECT_EQ(t, DefaultTermios());
- EXPECT_THAT(ioctl(main_.get(), TCGETS, &t), SyscallSucceeds());
+ EXPECT_THAT(ioctl(master_.get(), TCGETS, &t), SyscallSucceeds());
EXPECT_EQ(t, DefaultTermios());
}
-// Changing termios from the main actually affects the replica.
+// Changing termios from the master actually affects the replica.
//
-// TCSETS on the main actually redirects to the replica (see comment on
-// MainTermiosUnchangable).
+// TCSETS on the master actually redirects to the replica (see comment on
+// MasterTermiosUnchangable).
TEST_F(PtyTest, TermiosAffectsReplica) {
- struct kernel_termios main_termios = {};
- EXPECT_THAT(ioctl(main_.get(), TCGETS, &main_termios), SyscallSucceeds());
- main_termios.c_lflag ^= ICANON;
- EXPECT_THAT(ioctl(main_.get(), TCSETS, &main_termios), SyscallSucceeds());
+ struct kernel_termios master_termios = {};
+ EXPECT_THAT(ioctl(master_.get(), TCGETS, &master_termios), SyscallSucceeds());
+ master_termios.c_lflag ^= ICANON;
+ EXPECT_THAT(ioctl(master_.get(), TCSETS, &master_termios), SyscallSucceeds());
struct kernel_termios replica_termios = {};
EXPECT_THAT(ioctl(replica_.get(), TCGETS, &replica_termios),
SyscallSucceeds());
- EXPECT_EQ(main_termios, replica_termios);
+ EXPECT_EQ(master_termios, replica_termios);
}
-// The main end of the pty has termios:
+// The master end of the pty has termios:
//
// struct kernel_termios t = {
// .c_iflag = 0;
@@ -629,25 +630,25 @@ TEST_F(PtyTest, TermiosAffectsReplica) {
//
// (From drivers/tty/pty.c:unix98_pty_init)
//
-// All termios control ioctls on the main actually redirect to the replica
+// All termios control ioctls on the master actually redirect to the replica
// (drivers/tty/tty_ioctl.c:tty_mode_ioctl), making it impossible to change the
-// main termios.
+// master termios.
//
// Verify this by setting ICRNL (which rewrites input \r to \n) and verify that
-// it has no effect on the main.
-TEST_F(PtyTest, MainTermiosUnchangable) {
- struct kernel_termios main_termios = {};
- EXPECT_THAT(ioctl(main_.get(), TCGETS, &main_termios), SyscallSucceeds());
- main_termios.c_lflag |= ICRNL;
- EXPECT_THAT(ioctl(main_.get(), TCSETS, &main_termios), SyscallSucceeds());
+// it has no effect on the master.
+TEST_F(PtyTest, MasterTermiosUnchangable) {
+ struct kernel_termios master_termios = {};
+ EXPECT_THAT(ioctl(master_.get(), TCGETS, &master_termios), SyscallSucceeds());
+ master_termios.c_lflag |= ICRNL;
+ EXPECT_THAT(ioctl(master_.get(), TCSETS, &master_termios), SyscallSucceeds());
char c = '\r';
ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1));
- ExpectReadable(main_, 1, &c);
+ ExpectReadable(master_, 1, &c);
EXPECT_EQ(c, '\r'); // ICRNL had no effect!
- ExpectFinished(main_);
+ ExpectFinished(master_);
}
// ICRNL rewrites input \r to \n.
@@ -658,7 +659,7 @@ TEST_F(PtyTest, TermiosICRNL) {
ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
char c = '\r';
- ASSERT_THAT(WriteFd(main_.get(), &c, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1));
ExpectReadable(replica_, 1, &c);
EXPECT_EQ(c, '\n');
@@ -678,7 +679,7 @@ TEST_F(PtyTest, TermiosONLCR) {
// Extra byte for NUL for EXPECT_STREQ.
char buf[3] = {};
- ExpectReadable(main_, 2, buf);
+ ExpectReadable(master_, 2, buf);
EXPECT_STREQ(buf, "\r\n");
ExpectFinished(replica_);
@@ -691,7 +692,7 @@ TEST_F(PtyTest, TermiosIGNCR) {
ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
char c = '\r';
- ASSERT_THAT(WriteFd(main_.get(), &c, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1));
// Nothing to read.
ASSERT_THAT(PollAndReadFd(replica_.get(), &c, 1, kTimeout),
@@ -725,18 +726,18 @@ TEST_F(PtyTest, TermiosPollReplica) {
absl::SleepFor(absl::Seconds(1));
char s[] = "foo\n";
- ASSERT_THAT(WriteFd(main_.get(), s, strlen(s) + 1), SyscallSucceeds());
+ ASSERT_THAT(WriteFd(master_.get(), s, strlen(s) + 1), SyscallSucceeds());
}
-// Test that we can successfully poll for readable data from the main.
-TEST_F(PtyTest, TermiosPollMain) {
+// Test that we can successfully poll for readable data from the master.
+TEST_F(PtyTest, TermiosPollMaster) {
struct kernel_termios t = DefaultTermios();
t.c_iflag |= IGNCR;
t.c_lflag &= ~ICANON; // for byte-by-byte reading.
- ASSERT_THAT(ioctl(main_.get(), TCSETS, &t), SyscallSucceeds());
+ ASSERT_THAT(ioctl(master_.get(), TCSETS, &t), SyscallSucceeds());
absl::Notification notify;
- int mfd = main_.get();
+ int mfd = master_.get();
ScopedThread th([mfd, &notify]() {
notify.Notify();
@@ -765,7 +766,7 @@ TEST_F(PtyTest, TermiosINLCR) {
ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
char c = '\n';
- ASSERT_THAT(WriteFd(main_.get(), &c, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1));
ExpectReadable(replica_, 1, &c);
EXPECT_EQ(c, '\r');
@@ -784,7 +785,7 @@ TEST_F(PtyTest, TermiosONOCR) {
ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1));
// Nothing to read.
- ASSERT_THAT(PollAndReadFd(main_.get(), &c, 1, kTimeout),
+ ASSERT_THAT(PollAndReadFd(master_.get(), &c, 1, kTimeout),
PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out")));
// This time the column is greater than 0, so we should be able to read the CR
@@ -795,17 +796,17 @@ TEST_F(PtyTest, TermiosONOCR) {
SyscallSucceedsWithValue(kInputSize));
char buf[kInputSize] = {};
- ExpectReadable(main_, kInputSize, buf);
+ ExpectReadable(master_, kInputSize, buf);
EXPECT_EQ(memcmp(buf, kInput, kInputSize), 0);
- ExpectFinished(main_);
+ ExpectFinished(master_);
// Terminal should be at column 0 again, so no CR can be read.
ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1));
// Nothing to read.
- ASSERT_THAT(PollAndReadFd(main_.get(), &c, 1, kTimeout),
+ ASSERT_THAT(PollAndReadFd(master_.get(), &c, 1, kTimeout),
PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out")));
}
@@ -819,10 +820,10 @@ TEST_F(PtyTest, TermiosOCRNL) {
char c = '\r';
ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1));
- ExpectReadable(main_, 1, &c);
+ ExpectReadable(master_, 1, &c);
EXPECT_EQ(c, '\n');
- ExpectFinished(main_);
+ ExpectFinished(master_);
}
// Tests that VEOL is disabled when we start, and that we can set it to enable
@@ -830,7 +831,7 @@ TEST_F(PtyTest, TermiosOCRNL) {
TEST_F(PtyTest, VEOLTermination) {
// Write a few bytes ending with '\0', and confirm that we can't read.
constexpr char kInput[] = "hello";
- ASSERT_THAT(WriteFd(main_.get(), kInput, sizeof(kInput)),
+ ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)),
SyscallSucceedsWithValue(sizeof(kInput)));
char buf[sizeof(kInput)] = {};
ASSERT_THAT(PollAndReadFd(replica_.get(), buf, sizeof(kInput), kTimeout),
@@ -841,7 +842,7 @@ TEST_F(PtyTest, VEOLTermination) {
struct kernel_termios t = DefaultTermios();
t.c_cc[VEOL] = delim;
ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds());
- ASSERT_THAT(WriteFd(main_.get(), &delim, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1));
// Now we can read, as sending EOL caused the line to become available.
ExpectReadable(replica_, sizeof(kInput), buf);
@@ -861,7 +862,7 @@ TEST_F(PtyTest, CanonBigWrite) {
char input[kWriteLen];
memset(input, 'M', kWriteLen - 1);
input[kWriteLen - 1] = '\n';
- ASSERT_THAT(WriteFd(main_.get(), input, kWriteLen),
+ ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen),
SyscallSucceedsWithValue(kWriteLen));
// We can read the line.
@@ -877,7 +878,7 @@ TEST_F(PtyTest, SwitchCanonToNoncanon) {
// Write a few bytes without a terminating character, switch to noncanonical
// mode, and read them.
constexpr char kInput[] = "hello";
- ASSERT_THAT(WriteFd(main_.get(), kInput, sizeof(kInput)),
+ ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)),
SyscallSucceedsWithValue(sizeof(kInput)));
// Nothing available yet.
@@ -896,7 +897,7 @@ TEST_F(PtyTest, SwitchCanonToNoncanon) {
TEST_F(PtyTest, SwitchCanonToNonCanonNewline) {
// Write a few bytes with a terminating character.
constexpr char kInput[] = "hello\n";
- ASSERT_THAT(WriteFd(main_.get(), kInput, sizeof(kInput)),
+ ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)),
SyscallSucceedsWithValue(sizeof(kInput)));
DisableCanonical();
@@ -916,12 +917,12 @@ TEST_F(PtyTest, SwitchNoncanonToCanonNewlineBig) {
constexpr int kWriteLen = 4100;
char input[kWriteLen];
memset(input, 'M', kWriteLen);
- ASSERT_THAT(WriteFd(main_.get(), input, kWriteLen),
+ ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen),
SyscallSucceedsWithValue(kWriteLen));
// Wait for the input queue to fill.
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kMaxLineSize - 1));
constexpr char delim = '\n';
- ASSERT_THAT(WriteFd(main_.get(), &delim, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1));
EnableCanonical();
@@ -941,7 +942,7 @@ TEST_F(PtyTest, SwitchNoncanonToCanonNoNewline) {
// Write a few bytes without a terminating character.
// mode, and read them.
constexpr char kInput[] = "hello";
- ASSERT_THAT(WriteFd(main_.get(), kInput, sizeof(kInput) - 1),
+ ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput) - 1),
SyscallSucceedsWithValue(sizeof(kInput) - 1));
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), sizeof(kInput) - 1));
@@ -963,7 +964,7 @@ TEST_F(PtyTest, SwitchNoncanonToCanonNoNewlineBig) {
constexpr int kWriteLen = 4100;
char input[kWriteLen];
memset(input, 'M', kWriteLen);
- ASSERT_THAT(WriteFd(main_.get(), input, kWriteLen),
+ ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen),
SyscallSucceedsWithValue(kWriteLen));
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kMaxLineSize - 1));
@@ -987,12 +988,12 @@ TEST_F(PtyTest, NoncanonBigWrite) {
for (int i = 0; i < kInputSize; i++) {
// This makes too many syscalls for save/restore.
const DisableSave ds;
- ASSERT_THAT(WriteFd(main_.get(), &kInput, sizeof(kInput)),
+ ASSERT_THAT(WriteFd(master_.get(), &kInput, sizeof(kInput)),
SyscallSucceedsWithValue(sizeof(kInput)));
}
// We should be able to read out everything. Sleep a bit so that Linux has a
- // chance to move data from the main to the replica.
+ // chance to move data from the master to the replica.
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kMaxLineSize - 1));
for (int i = 0; i < kInputSize; i++) {
// This makes too many syscalls for save/restore.
@@ -1010,7 +1011,7 @@ TEST_F(PtyTest, NoncanonBigWrite) {
// Test newline.
TEST_F(PtyTest, TermiosICANONNewline) {
char input[3] = {'a', 'b', 'c'};
- ASSERT_THAT(WriteFd(main_.get(), input, sizeof(input)),
+ ASSERT_THAT(WriteFd(master_.get(), input, sizeof(input)),
SyscallSucceedsWithValue(sizeof(input)));
// Extra bytes for newline (written later) and NUL for EXPECT_STREQ.
@@ -1021,7 +1022,7 @@ TEST_F(PtyTest, TermiosICANONNewline) {
PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out")));
char delim = '\n';
- ASSERT_THAT(WriteFd(main_.get(), &delim, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1));
// Now it is available.
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), sizeof(input) + 1));
@@ -1036,7 +1037,7 @@ TEST_F(PtyTest, TermiosICANONNewline) {
// Test EOF (^D).
TEST_F(PtyTest, TermiosICANONEOF) {
char input[3] = {'a', 'b', 'c'};
- ASSERT_THAT(WriteFd(main_.get(), input, sizeof(input)),
+ ASSERT_THAT(WriteFd(master_.get(), input, sizeof(input)),
SyscallSucceedsWithValue(sizeof(input)));
// Extra byte for NUL for EXPECT_STREQ.
@@ -1046,7 +1047,7 @@ TEST_F(PtyTest, TermiosICANONEOF) {
ASSERT_THAT(PollAndReadFd(replica_.get(), buf, sizeof(input), kTimeout),
PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out")));
char delim = ControlCharacter('D');
- ASSERT_THAT(WriteFd(main_.get(), &delim, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1));
// Now it is available. Note that ^D is not included.
ExpectReadable(replica_, sizeof(input), buf);
@@ -1069,10 +1070,10 @@ TEST_F(PtyTest, CanonDiscard) {
// This makes too many syscalls for save/restore.
const DisableSave ds;
for (int i = 0; i < kInputSize; i++) {
- ASSERT_THAT(WriteFd(main_.get(), &kInput, sizeof(kInput)),
+ ASSERT_THAT(WriteFd(master_.get(), &kInput, sizeof(kInput)),
SyscallSucceedsWithValue(sizeof(kInput)));
}
- ASSERT_THAT(WriteFd(main_.get(), &delim, 1), SyscallSucceedsWithValue(1));
+ ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1));
}
// There should be multiple truncated lines available to read.
@@ -1091,9 +1092,9 @@ TEST_F(PtyTest, CanonMultiline) {
constexpr char kInput2[] = "BLUE\n";
// Write both lines.
- ASSERT_THAT(WriteFd(main_.get(), kInput1, sizeof(kInput1) - 1),
+ ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1),
SyscallSucceedsWithValue(sizeof(kInput1) - 1));
- ASSERT_THAT(WriteFd(main_.get(), kInput2, sizeof(kInput2) - 1),
+ ASSERT_THAT(WriteFd(master_.get(), kInput2, sizeof(kInput2) - 1),
SyscallSucceedsWithValue(sizeof(kInput2) - 1));
// Get the first line.
@@ -1117,9 +1118,9 @@ TEST_F(PtyTest, SwitchNoncanonToCanonMultiline) {
constexpr char kExpected[] = "GO\nBLUE\n";
// Write both lines.
- ASSERT_THAT(WriteFd(main_.get(), kInput1, sizeof(kInput1) - 1),
+ ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1),
SyscallSucceedsWithValue(sizeof(kInput1) - 1));
- ASSERT_THAT(WriteFd(main_.get(), kInput2, sizeof(kInput2) - 1),
+ ASSERT_THAT(WriteFd(master_.get(), kInput2, sizeof(kInput2) - 1),
SyscallSucceedsWithValue(sizeof(kInput2) - 1));
ASSERT_NO_ERRNO(
@@ -1140,7 +1141,7 @@ TEST_F(PtyTest, SwitchTwiceMultiline) {
// Write each line.
for (const std::string& input : kInputs) {
- ASSERT_THAT(WriteFd(main_.get(), input.c_str(), input.size()),
+ ASSERT_THAT(WriteFd(master_.get(), input.c_str(), input.size()),
SyscallSucceedsWithValue(input.size()));
}
@@ -1162,7 +1163,7 @@ TEST_F(PtyTest, SwitchTwiceMultiline) {
TEST_F(PtyTest, QueueSize) {
// Write the line.
constexpr char kInput1[] = "GO\n";
- ASSERT_THAT(WriteFd(main_.get(), kInput1, sizeof(kInput1) - 1),
+ ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1),
SyscallSucceedsWithValue(sizeof(kInput1) - 1));
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), sizeof(kInput1) - 1));
@@ -1170,7 +1171,7 @@ TEST_F(PtyTest, QueueSize) {
// readable size.
char input[kMaxLineSize];
memset(input, 'M', kMaxLineSize);
- ASSERT_THAT(WriteFd(main_.get(), input, kMaxLineSize),
+ ASSERT_THAT(WriteFd(master_.get(), input, kMaxLineSize),
SyscallSucceedsWithValue(kMaxLineSize));
int inputBufSize = ASSERT_NO_ERRNO_AND_VALUE(
WaitUntilReceived(replica_.get(), sizeof(kInput1) - 1));
@@ -1192,10 +1193,11 @@ TEST_F(PtyTest, PartialBadBuffer) {
// Leave only one free byte in the buffer.
char* bad_buffer = buf + kPageSize - 1;
- // Write to the main.
+ // Write to the master.
constexpr char kBuf[] = "hello\n";
constexpr size_t size = sizeof(kBuf) - 1;
- EXPECT_THAT(WriteFd(main_.get(), kBuf, size), SyscallSucceedsWithValue(size));
+ EXPECT_THAT(WriteFd(master_.get(), kBuf, size),
+ SyscallSucceedsWithValue(size));
// Read from the replica into bad_buffer.
ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), size));
@@ -1207,14 +1209,14 @@ TEST_F(PtyTest, PartialBadBuffer) {
TEST_F(PtyTest, SimpleEcho) {
constexpr char kInput[] = "Mr. Eko";
- EXPECT_THAT(WriteFd(main_.get(), kInput, strlen(kInput)),
+ EXPECT_THAT(WriteFd(master_.get(), kInput, strlen(kInput)),
SyscallSucceedsWithValue(strlen(kInput)));
char buf[100] = {};
- ExpectReadable(main_, strlen(kInput), buf);
+ ExpectReadable(master_, strlen(kInput), buf);
EXPECT_STREQ(buf, kInput);
- ExpectFinished(main_);
+ ExpectFinished(master_);
}
TEST_F(PtyTest, GetWindowSize) {
@@ -1231,16 +1233,17 @@ TEST_F(PtyTest, SetReplicaWindowSize) {
ASSERT_THAT(ioctl(replica_.get(), TIOCSWINSZ, &ws), SyscallSucceeds());
struct winsize retrieved_ws = {};
- ASSERT_THAT(ioctl(main_.get(), TIOCGWINSZ, &retrieved_ws), SyscallSucceeds());
+ ASSERT_THAT(ioctl(master_.get(), TIOCGWINSZ, &retrieved_ws),
+ SyscallSucceeds());
EXPECT_EQ(retrieved_ws.ws_row, kRows);
EXPECT_EQ(retrieved_ws.ws_col, kCols);
}
-TEST_F(PtyTest, SetMainWindowSize) {
+TEST_F(PtyTest, SetMasterWindowSize) {
constexpr uint16_t kRows = 343;
constexpr uint16_t kCols = 2401;
struct winsize ws = {.ws_row = kRows, .ws_col = kCols};
- ASSERT_THAT(ioctl(main_.get(), TIOCSWINSZ, &ws), SyscallSucceeds());
+ ASSERT_THAT(ioctl(master_.get(), TIOCSWINSZ, &ws), SyscallSucceeds());
struct winsize retrieved_ws = {};
ASSERT_THAT(ioctl(replica_.get(), TIOCGWINSZ, &retrieved_ws),
@@ -1252,8 +1255,8 @@ TEST_F(PtyTest, SetMainWindowSize) {
class JobControlTest : public ::testing::Test {
protected:
void SetUp() override {
- main_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK));
- replica_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main_));
+ master_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK));
+ replica_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master_));
// Make this a session leader, which also drops the controlling terminal.
// In the gVisor test environment, this test will be run as the session
@@ -1277,15 +1280,15 @@ class JobControlTest : public ::testing::Test {
return PosixError(wstatus, "process returned");
}
- // Main and replica ends of the PTY. Non-blocking.
- FileDescriptor main_;
+ // Master and replica ends of the PTY. Non-blocking.
+ FileDescriptor master_;
FileDescriptor replica_;
};
-TEST_F(JobControlTest, SetTTYMain) {
+TEST_F(JobControlTest, SetTTYMaster) {
auto res = RunInChild([=]() {
TEST_PCHECK(setsid() >= 0);
- TEST_PCHECK(!ioctl(main_.get(), TIOCSCTTY, 0));
+ TEST_PCHECK(!ioctl(master_.get(), TIOCSCTTY, 0));
});
ASSERT_NO_ERRNO(res);
}
@@ -1360,7 +1363,7 @@ TEST_F(JobControlTest, ReleaseWrongTTY) {
auto res = RunInChild([=]() {
TEST_PCHECK(setsid() >= 0);
TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
- TEST_PCHECK(ioctl(main_.get(), TIOCNOTTY) < 0 && errno == ENOTTY);
+ TEST_PCHECK(ioctl(master_.get(), TIOCNOTTY) < 0 && errno == ENOTTY);
});
ASSERT_NO_ERRNO(res);
}
diff --git a/test/syscalls/linux/pty_root.cc b/test/syscalls/linux/pty_root.cc
index a534cf0bb..4ac648729 100644
--- a/test/syscalls/linux/pty_root.cc
+++ b/test/syscalls/linux/pty_root.cc
@@ -48,9 +48,9 @@ TEST(JobControlRootTest, StealTTY) {
ASSERT_THAT(setsid(), SyscallSucceeds());
}
- FileDescriptor main =
+ FileDescriptor master =
ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK));
- FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(main));
+ FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master));
// Make replica the controlling terminal.
ASSERT_THAT(ioctl(replica.get(), TIOCSCTTY, 0), SyscallSucceeds());
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index 425084228..ffcd90475 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -1116,6 +1116,9 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) {
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
+ // TODO(gvisor.dev/issue/1400): Remove this after SO_LINGER is fixed.
+ SKIP_IF(IsRunningOnGvisor());
+
// Create the listening socket.
const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc
index f4b69c46c..04356b780 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic.cc
@@ -1080,124 +1080,5 @@ TEST_P(TCPSocketPairTest, TCPResetDuringClose_NoRandomSave) {
}
}
-// Test setsockopt and getsockopt for a socket with SO_LINGER option.
-TEST_P(TCPSocketPairTest, SetAndGetLingerOption) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- // Check getsockopt before SO_LINGER option is set.
- struct linger got_linger = {-1, -1};
- socklen_t got_len = sizeof(got_linger);
-
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
- &got_linger, &got_len),
- SyscallSucceeds());
- ASSERT_THAT(got_len, sizeof(got_linger));
- struct linger want_linger = {};
- EXPECT_EQ(0, memcmp(&want_linger, &got_linger, got_len));
-
- // Set and get SO_LINGER with negative values.
- struct linger sl;
- sl.l_onoff = 1;
- sl.l_linger = -3;
- ASSERT_THAT(
- setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
- SyscallSucceeds());
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
- &got_linger, &got_len),
- SyscallSucceeds());
- ASSERT_EQ(got_len, sizeof(got_linger));
- EXPECT_EQ(sl.l_onoff, got_linger.l_onoff);
- // Linux returns a different value as it uses HZ to convert the seconds to
- // jiffies which overflows for negative values. We want to be compatible with
- // linux for getsockopt return value.
- if (IsRunningOnGvisor()) {
- EXPECT_EQ(sl.l_linger, got_linger.l_linger);
- }
-
- // Set and get SO_LINGER option with positive values.
- sl.l_onoff = 1;
- sl.l_linger = 5;
- ASSERT_THAT(
- setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
- SyscallSucceeds());
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
- &got_linger, &got_len),
- SyscallSucceeds());
- ASSERT_EQ(got_len, sizeof(got_linger));
- EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len));
-}
-
-// Test socket to disable SO_LINGER option.
-TEST_P(TCPSocketPairTest, SetOffLingerOption) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- // Set the SO_LINGER option.
- struct linger sl;
- sl.l_onoff = 1;
- sl.l_linger = 5;
- ASSERT_THAT(
- setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
- SyscallSucceeds());
-
- // Check getsockopt after SO_LINGER option is set.
- struct linger got_linger = {-1, -1};
- socklen_t got_len = sizeof(got_linger);
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
- &got_linger, &got_len),
- SyscallSucceeds());
- ASSERT_EQ(got_len, sizeof(got_linger));
- EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len));
-
- sl.l_onoff = 0;
- sl.l_linger = 5;
- ASSERT_THAT(
- setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
- SyscallSucceeds());
-
- // Check getsockopt after SO_LINGER option is set to zero.
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
- &got_linger, &got_len),
- SyscallSucceeds());
- ASSERT_EQ(got_len, sizeof(got_linger));
- EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len));
-}
-
-// Test close on dup'd socket with SO_LINGER option set.
-TEST_P(TCPSocketPairTest, CloseWithLingerOption) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- // Set the SO_LINGER option.
- struct linger sl;
- sl.l_onoff = 1;
- sl.l_linger = 5;
- ASSERT_THAT(
- setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
- SyscallSucceeds());
-
- // Check getsockopt after SO_LINGER option is set.
- struct linger got_linger = {-1, -1};
- socklen_t got_len = sizeof(got_linger);
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
- &got_linger, &got_len),
- SyscallSucceeds());
- ASSERT_EQ(got_len, sizeof(got_linger));
- EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len));
-
- FileDescriptor dupFd = FileDescriptor(dup(sockets->first_fd()));
- ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
- char buf[10] = {};
- // Write on dupFd should succeed as socket will not be closed until
- // all references are removed.
- ASSERT_THAT(RetryEINTR(write)(dupFd.get(), buf, sizeof(buf)),
- SyscallSucceedsWithValue(sizeof(buf)));
- ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)),
- SyscallFailsWithErrno(EBADF));
-
- // Close the socket.
- dupFd.reset();
- // Write on dupFd should fail as all references for socket are removed.
- ASSERT_THAT(RetryEINTR(write)(dupFd.get(), buf, sizeof(buf)),
- SyscallFailsWithErrno(EBADF));
-}
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc
index 6e4ecd680..bbe356116 100644
--- a/test/syscalls/linux/socket_ip_udp_generic.cc
+++ b/test/syscalls/linux/socket_ip_udp_generic.cc
@@ -450,35 +450,5 @@ TEST_P(UDPSocketPairTest, TClassRecvMismatch) {
SyscallFailsWithErrno(EOPNOTSUPP));
}
-// Test the SO_LINGER option can be set/get on udp socket.
-TEST_P(UDPSocketPairTest, SoLingerFail) {
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
- int level = SOL_SOCKET;
- int type = SO_LINGER;
-
- struct linger sl;
- sl.l_onoff = 1;
- sl.l_linger = 5;
- ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &sl, sizeof(sl)),
- SyscallSucceedsWithValue(0));
-
- struct linger got_linger = {};
- socklen_t length = sizeof(sl);
- ASSERT_THAT(
- getsockopt(sockets->first_fd(), level, type, &got_linger, &length),
- SyscallSucceedsWithValue(0));
-
- ASSERT_EQ(length, sizeof(got_linger));
- // Linux returns the values which are set in the SetSockOpt for SO_LINGER.
- // In gVisor, we do not store the linger values for UDP as SO_LINGER for UDP
- // is a no-op.
- if (IsRunningOnGvisor()) {
- struct linger want_linger = {};
- EXPECT_EQ(0, memcmp(&want_linger, &got_linger, length));
- } else {
- EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
- }
-}
-
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/statfs.cc b/test/syscalls/linux/statfs.cc
index 49f2f156c..99ab280fd 100644
--- a/test/syscalls/linux/statfs.cc
+++ b/test/syscalls/linux/statfs.cc
@@ -43,9 +43,6 @@ TEST(StatfsTest, InternalTmpfs) {
struct statfs st;
EXPECT_THAT(statfs(temp_file.path().c_str(), &st), SyscallSucceeds());
- // Note: We could be an overlay or goferfs on some configurations.
- EXPECT_TRUE(st.f_type == TMPFS_MAGIC || st.f_type == OVERLAYFS_SUPER_MAGIC ||
- st.f_type == V9FS_MAGIC);
}
TEST(StatfsTest, InternalDevShm) {
diff --git a/test/util/pty_util.cc b/test/util/pty_util.cc
index 5fa622922..2cf0bea74 100644
--- a/test/util/pty_util.cc
+++ b/test/util/pty_util.cc
@@ -23,25 +23,25 @@
namespace gvisor {
namespace testing {
-PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& main) {
- PosixErrorOr<int> n = ReplicaID(main);
+PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& master) {
+ PosixErrorOr<int> n = ReplicaID(master);
if (!n.ok()) {
return PosixErrorOr<FileDescriptor>(n.error());
}
return Open(absl::StrCat("/dev/pts/", n.ValueOrDie()), O_RDWR | O_NONBLOCK);
}
-PosixErrorOr<int> ReplicaID(const FileDescriptor& main) {
+PosixErrorOr<int> ReplicaID(const FileDescriptor& master) {
// Get pty index.
int n;
- int ret = ioctl(main.get(), TIOCGPTN, &n);
+ int ret = ioctl(master.get(), TIOCGPTN, &n);
if (ret < 0) {
return PosixError(errno, "ioctl(TIOCGPTN) failed");
}
// Unlock pts.
int unlock = 0;
- ret = ioctl(main.get(), TIOCSPTLCK, &unlock);
+ ret = ioctl(master.get(), TIOCSPTLCK, &unlock);
if (ret < 0) {
return PosixError(errno, "ioctl(TIOSPTLCK) failed");
}
diff --git a/test/util/pty_util.h b/test/util/pty_util.h
index dff6adab5..ed7658868 100644
--- a/test/util/pty_util.h
+++ b/test/util/pty_util.h
@@ -21,11 +21,11 @@
namespace gvisor {
namespace testing {
-// Opens the replica end of the passed main as R/W and nonblocking.
-PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& main);
+// Opens the replica end of the passed master as R/W and nonblocking.
+PosixErrorOr<FileDescriptor> OpenReplica(const FileDescriptor& master);
-// Get the number of the replica end of the main.
-PosixErrorOr<int> ReplicaID(const FileDescriptor& main);
+// Get the number of the replica end of the master.
+PosixErrorOr<int> ReplicaID(const FileDescriptor& master);
} // namespace testing
} // namespace gvisor