mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
[pty] Return enqueued data and then EIO if the replica is closed
PiperOrigin-RevId: 532496868
This commit is contained in:
@@ -106,6 +106,9 @@ type lineDiscipline struct {
|
||||
// handling certain special characters like backspace.
|
||||
column int
|
||||
|
||||
// numReplicas is the number of replica file descriptors.
|
||||
numReplicas int
|
||||
|
||||
// masterWaiter is used to wait on the master end of the TTY.
|
||||
masterWaiter waiter.Queue
|
||||
|
||||
@@ -269,6 +272,20 @@ func (l *lineDiscipline) outputQueueWrite(ctx context.Context, src usermem.IOSeq
|
||||
return 0, linuxerr.ErrWouldBlock
|
||||
}
|
||||
|
||||
// replicaOpen is called when a replica file descriptor is opened.
|
||||
func (l *lineDiscipline) replicaOpen() {
|
||||
l.termiosMu.Lock()
|
||||
defer l.termiosMu.Unlock()
|
||||
l.numReplicas++
|
||||
}
|
||||
|
||||
// replicaClose is called when a replica file descriptor is closed.
|
||||
func (l *lineDiscipline) replicaClose() {
|
||||
l.termiosMu.Lock()
|
||||
defer l.termiosMu.Unlock()
|
||||
l.numReplicas--
|
||||
}
|
||||
|
||||
// transformer is a helper interface to make it easier to stateify queue.
|
||||
type transformer interface {
|
||||
// transform functions require queue's mutex to be held.
|
||||
|
||||
@@ -110,6 +110,9 @@ func (q *queue) read(ctx context.Context, dst usermem.IOSequence, l *lineDiscipl
|
||||
defer q.mu.Unlock()
|
||||
|
||||
if !q.readable {
|
||||
if l.numReplicas == 0 {
|
||||
return 0, false, false, linuxerr.EIO
|
||||
}
|
||||
return 0, false, false, linuxerr.ErrWouldBlock
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ func (ri *replicaInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kern
|
||||
// ignored silently.
|
||||
_ = t.ThreadGroup().SetControllingTTY(fd.inode.t.replicaKTTY, false /* steal */, fd.vfsfd.IsReadable())
|
||||
}
|
||||
ri.t.ld.replicaOpen()
|
||||
return &fd.vfsfd, nil
|
||||
|
||||
}
|
||||
@@ -114,7 +115,9 @@ type replicaFileDescription struct {
|
||||
var _ vfs.FileDescriptionImpl = (*replicaFileDescription)(nil)
|
||||
|
||||
// Release implements fs.FileOperations.Release.
|
||||
func (rfd *replicaFileDescription) Release(ctx context.Context) {}
|
||||
func (rfd *replicaFileDescription) Release(ctx context.Context) {
|
||||
rfd.inode.t.ld.replicaClose()
|
||||
}
|
||||
|
||||
// EventRegister implements waiter.Waitable.EventRegister.
|
||||
func (rfd *replicaFileDescription) EventRegister(e *waiter.Entry) error {
|
||||
|
||||
@@ -650,6 +650,29 @@ TEST_F(PtyTest, WriteReplicaToMaster) {
|
||||
EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0);
|
||||
}
|
||||
|
||||
// Verifies that data enqueued into the replica is still readable by the master
|
||||
// after the replica is closed.
|
||||
TEST_F(PtyTest, WriteReplicaToMasterReadAfterReplicaClosed) {
|
||||
// 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));
|
||||
|
||||
// Close the replica.
|
||||
replica_.reset();
|
||||
|
||||
char buf[sizeof(kExpected)] = {};
|
||||
ExpectReadable(master_, sizeof(buf) - 1, buf);
|
||||
EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0);
|
||||
|
||||
// After all data has been read, the master should return EIO.
|
||||
char c;
|
||||
EXPECT_THAT(ReadFd(master_.get(), &c, 1), SyscallFailsWithErrno(EIO));
|
||||
}
|
||||
|
||||
TEST_F(PtyTest, WriteInvalidUTF8) {
|
||||
char c = 0xff;
|
||||
ASSERT_THAT(syscall(__NR_write, master_.get(), &c, sizeof(c)),
|
||||
|
||||
Reference in New Issue
Block a user