github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/devpts/master.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/unimpl" 27 "github.com/metacubex/gvisor/pkg/sentry/vfs" 28 "github.com/metacubex/gvisor/pkg/usermem" 29 "github.com/metacubex/gvisor/pkg/waiter" 30 ) 31 32 // masterInode is the inode for the master end of the Terminal. 33 // 34 // +stateify savable 35 type masterInode struct { 36 implStatFS 37 kernfs.InodeAttrs 38 kernfs.InodeNoopRefCount 39 kernfs.InodeNotAnonymous 40 kernfs.InodeNotDirectory 41 kernfs.InodeNotSymlink 42 kernfs.InodeWatches 43 44 locks vfs.FileLocks 45 46 // root is the devpts root inode. 47 root *rootInode 48 } 49 50 var _ kernfs.Inode = (*masterInode)(nil) 51 52 // Open implements kernfs.Inode.Open. 53 func (mi *masterInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 54 t, err := mi.root.allocateTerminal(ctx, rp.Credentials()) 55 if err != nil { 56 return nil, err 57 } 58 59 fd := &masterFileDescription{ 60 inode: mi, 61 t: t, 62 } 63 fd.LockFD.Init(&mi.locks) 64 if err := fd.vfsfd.Init(fd, opts.Flags, rp.Mount(), d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil { 65 return nil, err 66 } 67 return &fd.vfsfd, nil 68 } 69 70 // Stat implements kernfs.Inode.Stat. 71 func (mi *masterInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) { 72 statx, err := mi.InodeAttrs.Stat(ctx, vfsfs, opts) 73 if err != nil { 74 return linux.Statx{}, err 75 } 76 statx.Blksize = 1024 77 statx.RdevMajor = linux.TTYAUX_MAJOR 78 statx.RdevMinor = linux.PTMX_MINOR 79 return statx, nil 80 } 81 82 // SetStat implements kernfs.Inode.SetStat 83 func (mi *masterInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { 84 if opts.Stat.Mask&linux.STATX_SIZE != 0 { 85 return linuxerr.EINVAL 86 } 87 return mi.InodeAttrs.SetStat(ctx, vfsfs, creds, opts) 88 } 89 90 // +stateify savable 91 type masterFileDescription struct { 92 vfsfd vfs.FileDescription 93 vfs.FileDescriptionDefaultImpl 94 vfs.LockFD 95 96 inode *masterInode 97 t *Terminal 98 } 99 100 var _ vfs.FileDescriptionImpl = (*masterFileDescription)(nil) 101 102 // Release implements vfs.FileDescriptionImpl.Release. 103 func (mfd *masterFileDescription) Release(ctx context.Context) { 104 mfd.inode.root.masterClose(ctx, mfd.t) 105 } 106 107 // EventRegister implements waiter.Waitable.EventRegister. 108 func (mfd *masterFileDescription) EventRegister(e *waiter.Entry) error { 109 mfd.t.ld.masterWaiter.EventRegister(e) 110 return nil 111 } 112 113 // EventUnregister implements waiter.Waitable.EventUnregister. 114 func (mfd *masterFileDescription) EventUnregister(e *waiter.Entry) { 115 mfd.t.ld.masterWaiter.EventUnregister(e) 116 } 117 118 // Readiness implements waiter.Waitable.Readiness. 119 func (mfd *masterFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask { 120 return mfd.t.ld.masterReadiness() 121 } 122 123 // Epollable implements FileDescriptionImpl.Epollable. 124 func (mfd *masterFileDescription) Epollable() bool { 125 return true 126 } 127 128 // Read implements vfs.FileDescriptionImpl.Read. 129 func (mfd *masterFileDescription) Read(ctx context.Context, dst usermem.IOSequence, _ vfs.ReadOptions) (int64, error) { 130 return mfd.t.ld.outputQueueRead(ctx, dst) 131 } 132 133 // Write implements vfs.FileDescriptionImpl.Write. 134 func (mfd *masterFileDescription) Write(ctx context.Context, src usermem.IOSequence, _ vfs.WriteOptions) (int64, error) { 135 return mfd.t.ld.inputQueueWrite(ctx, src) 136 } 137 138 // Ioctl implements vfs.FileDescriptionImpl.Ioctl. 139 func (mfd *masterFileDescription) Ioctl(ctx context.Context, io usermem.IO, sysno uintptr, args arch.SyscallArguments) (uintptr, error) { 140 t := kernel.TaskFromContext(ctx) 141 if t == nil { 142 // ioctl(2) may only be called from a task goroutine. 143 return 0, linuxerr.ENOTTY 144 } 145 146 switch cmd := args[1].Uint(); cmd { 147 case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ 148 // Get the number of bytes in the output queue read buffer. 149 return 0, mfd.t.ld.outputQueueReadSize(t, io, args) 150 case linux.TCGETS: 151 // N.B. TCGETS on the master actually returns the configuration 152 // of the replica end. 153 return mfd.t.ld.getTermios(t, args) 154 case linux.TCSETS: 155 // N.B. TCSETS on the master actually affects the configuration 156 // of the replica end. 157 return mfd.t.ld.setTermios(t, args) 158 case linux.TCSETSW: 159 // TODO(b/29356795): This should drain the output queue first. 160 return mfd.t.ld.setTermios(t, args) 161 case linux.TCSETSF: 162 // TODO(b/29356795): This should drain the output queue and 163 // clear the input queue first. 164 return mfd.t.ld.setTermios(t, args) 165 case linux.TIOCGPTN: 166 nP := primitive.Uint32(mfd.t.n) 167 _, err := nP.CopyOut(t, args[2].Pointer()) 168 return 0, err 169 case linux.TIOCSPTLCK: 170 // TODO(b/29356795): Implement pty locking. For now just pretend we do. 171 return 0, nil 172 case linux.TIOCGWINSZ: 173 return 0, mfd.t.ld.windowSize(t, args) 174 case linux.TIOCSWINSZ: 175 return 0, mfd.t.ld.setWindowSize(t, args) 176 case linux.TIOCSCTTY: 177 // Make the given terminal the controlling terminal of the 178 // calling process. 179 steal := args[2].Int() == 1 180 return 0, t.ThreadGroup().SetControllingTTY(mfd.t.masterKTTY, steal, mfd.vfsfd.IsReadable()) 181 case linux.TIOCNOTTY: 182 // Release this process's controlling terminal. 183 return 0, t.ThreadGroup().ReleaseControllingTTY(mfd.t.masterKTTY) 184 case linux.TIOCGPGRP: 185 // Get the foreground process group id. 186 pgid, err := t.ThreadGroup().ForegroundProcessGroupID(mfd.t.masterKTTY) 187 if err != nil { 188 return 0, err 189 } 190 ret := primitive.Int32(pgid) 191 _, err = ret.CopyOut(t, args[2].Pointer()) 192 return 0, err 193 case linux.TIOCSPGRP: 194 // Set the foreground process group id. 195 var pgid primitive.Int32 196 if _, err := pgid.CopyIn(t, args[2].Pointer()); err != nil { 197 return 0, err 198 } 199 return 0, t.ThreadGroup().SetForegroundProcessGroupID(mfd.t.masterKTTY, kernel.ProcessGroupID(pgid)) 200 default: 201 maybeEmitUnimplementedEvent(ctx, sysno, cmd) 202 return 0, linuxerr.ENOTTY 203 } 204 } 205 206 // SetStat implements vfs.FileDescriptionImpl.SetStat. 207 func (mfd *masterFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 208 creds := auth.CredentialsFromContext(ctx) 209 fs := mfd.vfsfd.VirtualDentry().Mount().Filesystem() 210 return mfd.inode.SetStat(ctx, fs, creds, opts) 211 } 212 213 // Stat implements vfs.FileDescriptionImpl.Stat. 214 func (mfd *masterFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 215 fs := mfd.vfsfd.VirtualDentry().Mount().Filesystem() 216 return mfd.inode.Stat(ctx, fs, opts) 217 } 218 219 // maybeEmitUnimplementedEvent emits unimplemented event if cmd is valid. 220 func maybeEmitUnimplementedEvent(ctx context.Context, sysno uintptr, cmd uint32) { 221 switch cmd { 222 case linux.TCGETS, 223 linux.TCSETS, 224 linux.TCSETSW, 225 linux.TCSETSF, 226 linux.TIOCGWINSZ, 227 linux.TIOCSWINSZ, 228 linux.TIOCSETD, 229 linux.TIOCSBRK, 230 linux.TIOCCBRK, 231 linux.TCSBRK, 232 linux.TCSBRKP, 233 linux.TIOCSTI, 234 linux.TIOCCONS, 235 linux.FIONBIO, 236 linux.TIOCEXCL, 237 linux.TIOCNXCL, 238 linux.TIOCGEXCL, 239 linux.TIOCGSID, 240 linux.TIOCGETD, 241 linux.TIOCVHANGUP, 242 linux.TIOCGDEV, 243 linux.TIOCMGET, 244 linux.TIOCMSET, 245 linux.TIOCMBIC, 246 linux.TIOCMBIS, 247 linux.TIOCGICOUNT, 248 linux.TCFLSH, 249 linux.TIOCSSERIAL, 250 linux.TIOCGPTPEER: 251 252 unimpl.EmitUnimplementedEvent(ctx, sysno) 253 } 254 }