github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/tty/master.go (about) 1 // Copyright 2018 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 tty 16 17 import ( 18 "github.com/SagerNet/gvisor/pkg/abi/linux" 19 "github.com/SagerNet/gvisor/pkg/context" 20 "github.com/SagerNet/gvisor/pkg/marshal/primitive" 21 "github.com/SagerNet/gvisor/pkg/sentry/arch" 22 "github.com/SagerNet/gvisor/pkg/sentry/fs" 23 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 24 "github.com/SagerNet/gvisor/pkg/sentry/kernel" 25 "github.com/SagerNet/gvisor/pkg/sentry/unimpl" 26 "github.com/SagerNet/gvisor/pkg/syserror" 27 "github.com/SagerNet/gvisor/pkg/usermem" 28 "github.com/SagerNet/gvisor/pkg/waiter" 29 ) 30 31 // LINT.IfChange 32 33 // masterInodeOperations are the fs.InodeOperations for the master end of the 34 // Terminal (ptmx file). 35 // 36 // +stateify savable 37 type masterInodeOperations struct { 38 fsutil.SimpleFileInode 39 40 // d is the containing dir. 41 d *dirInodeOperations 42 } 43 44 var _ fs.InodeOperations = (*masterInodeOperations)(nil) 45 46 // newMasterInode creates an Inode for the master end of a terminal. 47 func newMasterInode(ctx context.Context, d *dirInodeOperations, owner fs.FileOwner, p fs.FilePermissions) *fs.Inode { 48 iops := &masterInodeOperations{ 49 SimpleFileInode: *fsutil.NewSimpleFileInode(ctx, owner, p, linux.DEVPTS_SUPER_MAGIC), 50 d: d, 51 } 52 53 return fs.NewInode(ctx, iops, d.msrc, fs.StableAttr{ 54 DeviceID: ptsDevice.DeviceID(), 55 // N.B. Linux always uses inode id 2 for ptmx. See 56 // fs/devpts/inode.c:mknod_ptmx. 57 // 58 // TODO(b/75267214): Since ptsDevice must be shared between 59 // different mounts, we must not assign fixed numbers. 60 InodeID: ptsDevice.NextIno(), 61 Type: fs.CharacterDevice, 62 // See fs/devpts/inode.c:devpts_fill_super. 63 BlockSize: 1024, 64 // The PTY master effectively has two different major/minor 65 // device numbers. 66 // 67 // This one is returned by stat for both opened and unopened 68 // instances of this inode. 69 // 70 // When the inode is opened (GetFile), a new device number is 71 // allocated based on major UNIX98_PTY_MASTER_MAJOR and the tty 72 // index as minor number. However, this device number is only 73 // accessible via ioctl(TIOCGDEV) and /proc/TID/stat. 74 DeviceFileMajor: linux.TTYAUX_MAJOR, 75 DeviceFileMinor: linux.PTMX_MINOR, 76 }) 77 } 78 79 // Release implements fs.InodeOperations.Release. 80 func (mi *masterInodeOperations) Release(context.Context) { 81 } 82 83 // Truncate implements fs.InodeOperations.Truncate. 84 func (*masterInodeOperations) Truncate(context.Context, *fs.Inode, int64) error { 85 return nil 86 } 87 88 // GetFile implements fs.InodeOperations.GetFile. 89 // 90 // It allocates a new terminal. 91 func (mi *masterInodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) { 92 t, err := mi.d.allocateTerminal(ctx) 93 if err != nil { 94 return nil, err 95 } 96 97 return fs.NewFile(ctx, d, flags, &masterFileOperations{ 98 d: mi.d, 99 t: t, 100 }), nil 101 } 102 103 // masterFileOperations are the fs.FileOperations for the master end of a terminal. 104 // 105 // +stateify savable 106 type masterFileOperations struct { 107 fsutil.FilePipeSeek `state:"nosave"` 108 fsutil.FileNotDirReaddir `state:"nosave"` 109 fsutil.FileNoFsync `state:"nosave"` 110 fsutil.FileNoMMap `state:"nosave"` 111 fsutil.FileNoSplice `state:"nosave"` 112 fsutil.FileNoopFlush `state:"nosave"` 113 fsutil.FileUseInodeUnstableAttr `state:"nosave"` 114 115 // d is the containing dir. 116 d *dirInodeOperations 117 118 // t is the connected Terminal. 119 t *Terminal 120 } 121 122 var _ fs.FileOperations = (*masterFileOperations)(nil) 123 124 // Release implements fs.FileOperations.Release. 125 func (mf *masterFileOperations) Release(ctx context.Context) { 126 mf.d.masterClose(ctx, mf.t) 127 mf.t.DecRef(ctx) 128 } 129 130 // EventRegister implements waiter.Waitable.EventRegister. 131 func (mf *masterFileOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) { 132 mf.t.ld.masterWaiter.EventRegister(e, mask) 133 } 134 135 // EventUnregister implements waiter.Waitable.EventUnregister. 136 func (mf *masterFileOperations) EventUnregister(e *waiter.Entry) { 137 mf.t.ld.masterWaiter.EventUnregister(e) 138 } 139 140 // Readiness implements waiter.Waitable.Readiness. 141 func (mf *masterFileOperations) Readiness(mask waiter.EventMask) waiter.EventMask { 142 return mf.t.ld.masterReadiness() 143 } 144 145 // Read implements fs.FileOperations.Read. 146 func (mf *masterFileOperations) Read(ctx context.Context, _ *fs.File, dst usermem.IOSequence, _ int64) (int64, error) { 147 return mf.t.ld.outputQueueRead(ctx, dst) 148 } 149 150 // Write implements fs.FileOperations.Write. 151 func (mf *masterFileOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) { 152 return mf.t.ld.inputQueueWrite(ctx, src) 153 } 154 155 // Ioctl implements fs.FileOperations.Ioctl. 156 func (mf *masterFileOperations) Ioctl(ctx context.Context, file *fs.File, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { 157 t := kernel.TaskFromContext(ctx) 158 if t == nil { 159 // ioctl(2) may only be called from a task goroutine. 160 return 0, syserror.ENOTTY 161 } 162 163 switch cmd := args[1].Uint(); cmd { 164 case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ 165 // Get the number of bytes in the output queue read buffer. 166 return 0, mf.t.ld.outputQueueReadSize(t, args) 167 case linux.TCGETS: 168 // N.B. TCGETS on the master actually returns the configuration 169 // of the replica end. 170 return mf.t.ld.getTermios(t, args) 171 case linux.TCSETS: 172 // N.B. TCSETS on the master actually affects the configuration 173 // of the replica end. 174 return mf.t.ld.setTermios(t, args) 175 case linux.TCSETSW: 176 // TODO(b/29356795): This should drain the output queue first. 177 return mf.t.ld.setTermios(t, args) 178 case linux.TIOCGPTN: 179 nP := primitive.Uint32(mf.t.n) 180 _, err := nP.CopyOut(t, args[2].Pointer()) 181 return 0, err 182 case linux.TIOCSPTLCK: 183 // TODO(b/29356795): Implement pty locking. For now just pretend we do. 184 return 0, nil 185 case linux.TIOCGWINSZ: 186 return 0, mf.t.ld.windowSize(t, args) 187 case linux.TIOCSWINSZ: 188 return 0, mf.t.ld.setWindowSize(t, args) 189 case linux.TIOCSCTTY: 190 // Make the given terminal the controlling terminal of the 191 // calling process. 192 return 0, mf.t.setControllingTTY(ctx, args, true /* isMaster */, file.Flags().Read) 193 case linux.TIOCNOTTY: 194 // Release this process's controlling terminal. 195 return 0, mf.t.releaseControllingTTY(ctx, args, true /* isMaster */) 196 case linux.TIOCGPGRP: 197 // Get the foreground process group. 198 return mf.t.foregroundProcessGroup(ctx, args, true /* isMaster */) 199 case linux.TIOCSPGRP: 200 // Set the foreground process group. 201 return mf.t.setForegroundProcessGroup(ctx, args, true /* isMaster */) 202 default: 203 maybeEmitUnimplementedEvent(ctx, cmd) 204 return 0, syserror.ENOTTY 205 } 206 } 207 208 // maybeEmitUnimplementedEvent emits unimplemented event if cmd is valid. 209 func maybeEmitUnimplementedEvent(ctx context.Context, cmd uint32) { 210 switch cmd { 211 case linux.TCGETS, 212 linux.TCSETS, 213 linux.TCSETSW, 214 linux.TCSETSF, 215 linux.TIOCGWINSZ, 216 linux.TIOCSWINSZ, 217 linux.TIOCSETD, 218 linux.TIOCSBRK, 219 linux.TIOCCBRK, 220 linux.TCSBRK, 221 linux.TCSBRKP, 222 linux.TIOCSTI, 223 linux.TIOCCONS, 224 linux.FIONBIO, 225 linux.TIOCEXCL, 226 linux.TIOCNXCL, 227 linux.TIOCGEXCL, 228 linux.TIOCGSID, 229 linux.TIOCGETD, 230 linux.TIOCVHANGUP, 231 linux.TIOCGDEV, 232 linux.TIOCMGET, 233 linux.TIOCMSET, 234 linux.TIOCMBIC, 235 linux.TIOCMBIS, 236 linux.TIOCGICOUNT, 237 linux.TCFLSH, 238 linux.TIOCSSERIAL, 239 linux.TIOCGPTPEER: 240 241 unimpl.EmitUnimplementedEvent(ctx) 242 } 243 } 244 245 // LINT.ThenChange(../../fsimpl/devpts/master.go)