github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/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/metacubex/gvisor/pkg/abi/linux" 19 "github.com/metacubex/gvisor/pkg/context" 20 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 21 "github.com/metacubex/gvisor/pkg/marshal/primitive" 22 "github.com/metacubex/gvisor/pkg/sentry/arch" 23 "github.com/metacubex/gvisor/pkg/sentry/fsimpl/kernfs" 24 "github.com/metacubex/gvisor/pkg/sentry/kernel" 25 "github.com/metacubex/gvisor/pkg/sentry/kernel/auth" 26 "github.com/metacubex/gvisor/pkg/sentry/vfs" 27 "github.com/metacubex/gvisor/pkg/usermem" 28 "github.com/metacubex/gvisor/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.TCSETSF: 174 // TODO(b/29356795): This should drain the output queue and 175 // clear the input queue first. 176 return rfd.inode.t.ld.setTermios(t, args) 177 case linux.TIOCGPTN: 178 nP := primitive.Uint32(rfd.inode.t.n) 179 _, err := nP.CopyOut(t, args[2].Pointer()) 180 return 0, err 181 case linux.TIOCGWINSZ: 182 return 0, rfd.inode.t.ld.windowSize(t, args) 183 case linux.TIOCSWINSZ: 184 return 0, rfd.inode.t.ld.setWindowSize(t, args) 185 case linux.TIOCSCTTY: 186 // Make the given terminal the controlling terminal of the 187 // calling process. 188 steal := args[2].Int() == 1 189 return 0, t.ThreadGroup().SetControllingTTY(rfd.inode.t.replicaKTTY, steal, rfd.vfsfd.IsReadable()) 190 case linux.TIOCNOTTY: 191 // Release this process's controlling terminal. 192 return 0, t.ThreadGroup().ReleaseControllingTTY(rfd.inode.t.replicaKTTY) 193 case linux.TIOCGPGRP: 194 // Get the foreground process group id. 195 pgid, err := t.ThreadGroup().ForegroundProcessGroupID(rfd.inode.t.replicaKTTY) 196 if err != nil { 197 return 0, err 198 } 199 ret := primitive.Int32(pgid) 200 _, err = ret.CopyOut(t, args[2].Pointer()) 201 return 0, err 202 case linux.TIOCSPGRP: 203 // Set the foreground process group id. 204 var pgid primitive.Int32 205 if _, err := pgid.CopyIn(t, args[2].Pointer()); err != nil { 206 return 0, err 207 } 208 return 0, t.ThreadGroup().SetForegroundProcessGroupID(rfd.inode.t.replicaKTTY, kernel.ProcessGroupID(pgid)) 209 default: 210 maybeEmitUnimplementedEvent(ctx, sysno, cmd) 211 return 0, linuxerr.ENOTTY 212 } 213 } 214 215 // SetStat implements vfs.FileDescriptionImpl.SetStat. 216 func (rfd *replicaFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 217 creds := auth.CredentialsFromContext(ctx) 218 fs := rfd.vfsfd.VirtualDentry().Mount().Filesystem() 219 return rfd.inode.SetStat(ctx, fs, creds, opts) 220 } 221 222 // Stat implements vfs.FileDescriptionImpl.Stat. 223 func (rfd *replicaFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 224 fs := rfd.vfsfd.VirtualDentry().Mount().Filesystem() 225 return rfd.inode.Stat(ctx, fs, opts) 226 }