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