github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/pkg/abi/linux" 19 "github.com/nicocha30/gvisor-ligolo/pkg/context" 20 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 21 "github.com/nicocha30/gvisor-ligolo/pkg/marshal/primitive" 22 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/arch" 23 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsimpl/kernfs" 24 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 25 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 26 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/unimpl" 27 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 28 "github.com/nicocha30/gvisor-ligolo/pkg/usermem" 29 "github.com/nicocha30/gvisor-ligolo/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.TIOCGPTN: 162 nP := primitive.Uint32(mfd.t.n) 163 _, err := nP.CopyOut(t, args[2].Pointer()) 164 return 0, err 165 case linux.TIOCSPTLCK: 166 // TODO(b/29356795): Implement pty locking. For now just pretend we do. 167 return 0, nil 168 case linux.TIOCGWINSZ: 169 return 0, mfd.t.ld.windowSize(t, args) 170 case linux.TIOCSWINSZ: 171 return 0, mfd.t.ld.setWindowSize(t, args) 172 case linux.TIOCSCTTY: 173 // Make the given terminal the controlling terminal of the 174 // calling process. 175 steal := args[2].Int() == 1 176 return 0, t.ThreadGroup().SetControllingTTY(mfd.t.masterKTTY, steal, mfd.vfsfd.IsReadable()) 177 case linux.TIOCNOTTY: 178 // Release this process's controlling terminal. 179 return 0, t.ThreadGroup().ReleaseControllingTTY(mfd.t.masterKTTY) 180 case linux.TIOCGPGRP: 181 // Get the foreground process group id. 182 pgid, err := t.ThreadGroup().ForegroundProcessGroupID(mfd.t.masterKTTY) 183 if err != nil { 184 return 0, err 185 } 186 ret := primitive.Int32(pgid) 187 _, err = ret.CopyOut(t, args[2].Pointer()) 188 return 0, err 189 case linux.TIOCSPGRP: 190 // Set the foreground process group id. 191 var pgid primitive.Int32 192 if _, err := pgid.CopyIn(t, args[2].Pointer()); err != nil { 193 return 0, err 194 } 195 return 0, t.ThreadGroup().SetForegroundProcessGroupID(mfd.t.masterKTTY, kernel.ProcessGroupID(pgid)) 196 default: 197 maybeEmitUnimplementedEvent(ctx, sysno, cmd) 198 return 0, linuxerr.ENOTTY 199 } 200 } 201 202 // SetStat implements vfs.FileDescriptionImpl.SetStat. 203 func (mfd *masterFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 204 creds := auth.CredentialsFromContext(ctx) 205 fs := mfd.vfsfd.VirtualDentry().Mount().Filesystem() 206 return mfd.inode.SetStat(ctx, fs, creds, opts) 207 } 208 209 // Stat implements vfs.FileDescriptionImpl.Stat. 210 func (mfd *masterFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 211 fs := mfd.vfsfd.VirtualDentry().Mount().Filesystem() 212 return mfd.inode.Stat(ctx, fs, opts) 213 } 214 215 // maybeEmitUnimplementedEvent emits unimplemented event if cmd is valid. 216 func maybeEmitUnimplementedEvent(ctx context.Context, sysno uintptr, cmd uint32) { 217 switch cmd { 218 case linux.TCGETS, 219 linux.TCSETS, 220 linux.TCSETSW, 221 linux.TCSETSF, 222 linux.TIOCGWINSZ, 223 linux.TIOCSWINSZ, 224 linux.TIOCSETD, 225 linux.TIOCSBRK, 226 linux.TIOCCBRK, 227 linux.TCSBRK, 228 linux.TCSBRKP, 229 linux.TIOCSTI, 230 linux.TIOCCONS, 231 linux.FIONBIO, 232 linux.TIOCEXCL, 233 linux.TIOCNXCL, 234 linux.TIOCGEXCL, 235 linux.TIOCGSID, 236 linux.TIOCGETD, 237 linux.TIOCVHANGUP, 238 linux.TIOCGDEV, 239 linux.TIOCMGET, 240 linux.TIOCMSET, 241 linux.TIOCMBIC, 242 linux.TIOCMBIS, 243 linux.TIOCGICOUNT, 244 linux.TCFLSH, 245 linux.TIOCSSERIAL, 246 linux.TIOCGPTPEER: 247 248 unimpl.EmitUnimplementedEvent(ctx, sysno) 249 } 250 }