github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/fsimpl/devpts/replica.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package devpts 16 17 import ( 18 "github.com/ttpreport/gvisor-ligolo/pkg/abi/linux" 19 "github.com/ttpreport/gvisor-ligolo/pkg/context" 20 "github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr" 21 "github.com/ttpreport/gvisor-ligolo/pkg/marshal/primitive" 22 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/arch" 23 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/fsimpl/kernfs" 24 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel" 25 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel/auth" 26 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/vfs" 27 "github.com/ttpreport/gvisor-ligolo/pkg/usermem" 28 "github.com/ttpreport/gvisor-ligolo/pkg/waiter" 29 ) 30 31 // replicaInode is the inode for the replica end of the Terminal. 32 // 33 // +stateify savable 34 type replicaInode struct { 35 implStatFS 36 kernfs.InodeAttrs 37 kernfs.InodeNoopRefCount 38 kernfs.InodeNotAnonymous 39 kernfs.InodeNotDirectory 40 kernfs.InodeNotSymlink 41 kernfs.InodeWatches 42 43 locks vfs.FileLocks 44 45 // root is the devpts root inode. 46 root *rootInode 47 48 // t is the connected Terminal. 49 t *Terminal 50 } 51 52 var _ kernfs.Inode = (*replicaInode)(nil) 53 54 // Open implements kernfs.Inode.Open. 55 func (ri *replicaInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 56 t := kernel.TaskFromContext(ctx) 57 if t == nil { 58 panic("open must be called from a task goroutine") 59 } 60 fd := &replicaFileDescription{ 61 inode: ri, 62 } 63 fd.LockFD.Init(&ri.locks) 64 if err := fd.vfsfd.Init(fd, opts.Flags, rp.Mount(), d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil { 65 return nil, err 66 } 67 if opts.Flags&linux.O_NOCTTY == 0 { 68 // Opening a replica sets the process' controlling TTY when 69 // possible. An error indicates it cannot be set, and is 70 // ignored silently. 71 _ = t.ThreadGroup().SetControllingTTY(fd.inode.t.replicaKTTY, false /* steal */, fd.vfsfd.IsReadable()) 72 } 73 ri.t.ld.replicaOpen() 74 return &fd.vfsfd, nil 75 76 } 77 78 // Valid implements kernfs.Inode.Valid. 79 func (ri *replicaInode) Valid(context.Context) bool { 80 // Return valid if the replica still exists. 81 ri.root.mu.Lock() 82 defer ri.root.mu.Unlock() 83 _, ok := ri.root.replicas[ri.t.n] 84 return ok 85 } 86 87 // Stat implements kernfs.Inode.Stat. 88 func (ri *replicaInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) { 89 statx, err := ri.InodeAttrs.Stat(ctx, vfsfs, opts) 90 if err != nil { 91 return linux.Statx{}, err 92 } 93 statx.Blksize = 1024 94 statx.RdevMajor = linux.UNIX98_PTY_REPLICA_MAJOR 95 statx.RdevMinor = ri.t.n 96 return statx, nil 97 } 98 99 // SetStat implements kernfs.Inode.SetStat 100 func (ri *replicaInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { 101 if opts.Stat.Mask&linux.STATX_SIZE != 0 { 102 return linuxerr.EINVAL 103 } 104 return ri.InodeAttrs.SetStat(ctx, vfsfs, creds, opts) 105 } 106 107 // +stateify savable 108 type replicaFileDescription struct { 109 vfsfd vfs.FileDescription 110 vfs.FileDescriptionDefaultImpl 111 vfs.LockFD 112 113 inode *replicaInode 114 } 115 116 var _ vfs.FileDescriptionImpl = (*replicaFileDescription)(nil) 117 118 // Release implements fs.FileOperations.Release. 119 func (rfd *replicaFileDescription) Release(ctx context.Context) { 120 rfd.inode.t.ld.replicaClose() 121 } 122 123 // EventRegister implements waiter.Waitable.EventRegister. 124 func (rfd *replicaFileDescription) EventRegister(e *waiter.Entry) error { 125 rfd.inode.t.ld.replicaWaiter.EventRegister(e) 126 return nil 127 } 128 129 // EventUnregister implements waiter.Waitable.EventUnregister. 130 func (rfd *replicaFileDescription) EventUnregister(e *waiter.Entry) { 131 rfd.inode.t.ld.replicaWaiter.EventUnregister(e) 132 } 133 134 // Readiness implements waiter.Waitable.Readiness. 135 func (rfd *replicaFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask { 136 return rfd.inode.t.ld.replicaReadiness() 137 } 138 139 // Epollable implements FileDescriptionImpl.Epollable. 140 func (rfd *replicaFileDescription) Epollable() bool { 141 return true 142 } 143 144 // Read implements vfs.FileDescriptionImpl.Read. 145 func (rfd *replicaFileDescription) Read(ctx context.Context, dst usermem.IOSequence, _ vfs.ReadOptions) (int64, error) { 146 return rfd.inode.t.ld.inputQueueRead(ctx, dst) 147 } 148 149 // Write implements vfs.FileDescriptionImpl.Write. 150 func (rfd *replicaFileDescription) Write(ctx context.Context, src usermem.IOSequence, _ vfs.WriteOptions) (int64, error) { 151 return rfd.inode.t.ld.outputQueueWrite(ctx, src) 152 } 153 154 // Ioctl implements vfs.FileDescriptionImpl.Ioctl. 155 func (rfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { 156 t := kernel.TaskFromContext(ctx) 157 if t == nil { 158 // ioctl(2) may only be called from a task goroutine. 159 return 0, linuxerr.ENOTTY 160 } 161 162 switch cmd := args[1].Uint(); cmd { 163 case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ 164 // Get the number of bytes in the input queue read buffer. 165 return 0, rfd.inode.t.ld.inputQueueReadSize(t, io, args) 166 case linux.TCGETS: 167 return rfd.inode.t.ld.getTermios(t, args) 168 case linux.TCSETS: 169 return rfd.inode.t.ld.setTermios(t, args) 170 case linux.TCSETSW: 171 // TODO(b/29356795): This should drain the output queue first. 172 return rfd.inode.t.ld.setTermios(t, args) 173 case linux.TIOCGPTN: 174 nP := primitive.Uint32(rfd.inode.t.n) 175 _, err := nP.CopyOut(t, args[2].Pointer()) 176 return 0, err 177 case linux.TIOCGWINSZ: 178 return 0, rfd.inode.t.ld.windowSize(t, args) 179 case linux.TIOCSWINSZ: 180 return 0, rfd.inode.t.ld.setWindowSize(t, args) 181 case linux.TIOCSCTTY: 182 // Make the given terminal the controlling terminal of the 183 // calling process. 184 steal := args[2].Int() == 1 185 return 0, t.ThreadGroup().SetControllingTTY(rfd.inode.t.replicaKTTY, steal, rfd.vfsfd.IsReadable()) 186 case linux.TIOCNOTTY: 187 // Release this process's controlling terminal. 188 return 0, t.ThreadGroup().ReleaseControllingTTY(rfd.inode.t.replicaKTTY) 189 case linux.TIOCGPGRP: 190 // Get the foreground process group id. 191 pgid, err := t.ThreadGroup().ForegroundProcessGroupID(rfd.inode.t.replicaKTTY) 192 if err != nil { 193 return 0, err 194 } 195 ret := primitive.Int32(pgid) 196 _, err = ret.CopyOut(t, args[2].Pointer()) 197 return 0, err 198 case linux.TIOCSPGRP: 199 // Set the foreground process group id. 200 var pgid primitive.Int32 201 if _, err := pgid.CopyIn(t, args[2].Pointer()); err != nil { 202 return 0, err 203 } 204 return 0, t.ThreadGroup().SetForegroundProcessGroupID(rfd.inode.t.replicaKTTY, kernel.ProcessGroupID(pgid)) 205 default: 206 maybeEmitUnimplementedEvent(ctx, sysno, cmd) 207 return 0, linuxerr.ENOTTY 208 } 209 } 210 211 // SetStat implements vfs.FileDescriptionImpl.SetStat. 212 func (rfd *replicaFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 213 creds := auth.CredentialsFromContext(ctx) 214 fs := rfd.vfsfd.VirtualDentry().Mount().Filesystem() 215 return rfd.inode.SetStat(ctx, fs, creds, opts) 216 } 217 218 // Stat implements vfs.FileDescriptionImpl.Stat. 219 func (rfd *replicaFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 220 fs := rfd.vfsfd.VirtualDentry().Mount().Filesystem() 221 return rfd.inode.Stat(ctx, fs, opts) 222 }