github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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/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/vfs" 27 "github.com/SagerNet/gvisor/pkg/syserror" 28 "github.com/SagerNet/gvisor/pkg/usermem" 29 "github.com/SagerNet/gvisor/pkg/waiter" 30 ) 31 32 // replicaInode is the inode for the replica end of the Terminal. 33 // 34 // +stateify savable 35 type replicaInode struct { 36 implStatFS 37 kernfs.InodeAttrs 38 kernfs.InodeNoopRefCount 39 kernfs.InodeNotDirectory 40 kernfs.InodeNotSymlink 41 42 locks vfs.FileLocks 43 44 // root is the devpts root inode. 45 root *rootInode 46 47 // t is the connected Terminal. 48 t *Terminal 49 } 50 51 var _ kernfs.Inode = (*replicaInode)(nil) 52 53 // Open implements kernfs.Inode.Open. 54 func (ri *replicaInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) { 55 fd := &replicaFileDescription{ 56 inode: ri, 57 } 58 fd.LockFD.Init(&ri.locks) 59 if err := fd.vfsfd.Init(fd, opts.Flags, rp.Mount(), d.VFSDentry(), &vfs.FileDescriptionOptions{}); err != nil { 60 return nil, err 61 } 62 if opts.Flags&linux.O_NOCTTY == 0 { 63 // Opening a replica sets the process' controlling TTY when 64 // possible. An error indicates it cannot be set, and is 65 // ignored silently. 66 _ = fd.inode.t.setControllingTTY(ctx, false /* steal */, false /* isMaster */, fd.vfsfd.IsReadable()) 67 } 68 return &fd.vfsfd, nil 69 70 } 71 72 // Valid implements kernfs.Inode.Valid. 73 func (ri *replicaInode) Valid(context.Context) bool { 74 // Return valid if the replica still exists. 75 ri.root.mu.Lock() 76 defer ri.root.mu.Unlock() 77 _, ok := ri.root.replicas[ri.t.n] 78 return ok 79 } 80 81 // Stat implements kernfs.Inode.Stat. 82 func (ri *replicaInode) Stat(ctx context.Context, vfsfs *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, error) { 83 statx, err := ri.InodeAttrs.Stat(ctx, vfsfs, opts) 84 if err != nil { 85 return linux.Statx{}, err 86 } 87 statx.Blksize = 1024 88 statx.RdevMajor = linux.UNIX98_PTY_REPLICA_MAJOR 89 statx.RdevMinor = ri.t.n 90 return statx, nil 91 } 92 93 // SetStat implements kernfs.Inode.SetStat 94 func (ri *replicaInode) SetStat(ctx context.Context, vfsfs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error { 95 if opts.Stat.Mask&linux.STATX_SIZE != 0 { 96 return linuxerr.EINVAL 97 } 98 return ri.InodeAttrs.SetStat(ctx, vfsfs, creds, opts) 99 } 100 101 // +stateify savable 102 type replicaFileDescription struct { 103 vfsfd vfs.FileDescription 104 vfs.FileDescriptionDefaultImpl 105 vfs.LockFD 106 107 inode *replicaInode 108 } 109 110 var _ vfs.FileDescriptionImpl = (*replicaFileDescription)(nil) 111 112 // Release implements fs.FileOperations.Release. 113 func (rfd *replicaFileDescription) Release(ctx context.Context) {} 114 115 // EventRegister implements waiter.Waitable.EventRegister. 116 func (rfd *replicaFileDescription) EventRegister(e *waiter.Entry, mask waiter.EventMask) { 117 rfd.inode.t.ld.replicaWaiter.EventRegister(e, mask) 118 } 119 120 // EventUnregister implements waiter.Waitable.EventUnregister. 121 func (rfd *replicaFileDescription) EventUnregister(e *waiter.Entry) { 122 rfd.inode.t.ld.replicaWaiter.EventUnregister(e) 123 } 124 125 // Readiness implements waiter.Waitable.Readiness. 126 func (rfd *replicaFileDescription) Readiness(mask waiter.EventMask) waiter.EventMask { 127 return rfd.inode.t.ld.replicaReadiness() 128 } 129 130 // Read implements vfs.FileDescriptionImpl.Read. 131 func (rfd *replicaFileDescription) Read(ctx context.Context, dst usermem.IOSequence, _ vfs.ReadOptions) (int64, error) { 132 return rfd.inode.t.ld.inputQueueRead(ctx, dst) 133 } 134 135 // Write implements vfs.FileDescriptionImpl.Write. 136 func (rfd *replicaFileDescription) Write(ctx context.Context, src usermem.IOSequence, _ vfs.WriteOptions) (int64, error) { 137 return rfd.inode.t.ld.outputQueueWrite(ctx, src) 138 } 139 140 // Ioctl implements vfs.FileDescriptionImpl.Ioctl. 141 func (rfd *replicaFileDescription) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallArguments) (uintptr, error) { 142 t := kernel.TaskFromContext(ctx) 143 if t == nil { 144 // ioctl(2) may only be called from a task goroutine. 145 return 0, syserror.ENOTTY 146 } 147 148 switch cmd := args[1].Uint(); cmd { 149 case linux.FIONREAD: // linux.FIONREAD == linux.TIOCINQ 150 // Get the number of bytes in the input queue read buffer. 151 return 0, rfd.inode.t.ld.inputQueueReadSize(t, io, args) 152 case linux.TCGETS: 153 return rfd.inode.t.ld.getTermios(t, args) 154 case linux.TCSETS: 155 return rfd.inode.t.ld.setTermios(t, args) 156 case linux.TCSETSW: 157 // TODO(b/29356795): This should drain the output queue first. 158 return rfd.inode.t.ld.setTermios(t, args) 159 case linux.TIOCGPTN: 160 nP := primitive.Uint32(rfd.inode.t.n) 161 _, err := nP.CopyOut(t, args[2].Pointer()) 162 return 0, err 163 case linux.TIOCGWINSZ: 164 return 0, rfd.inode.t.ld.windowSize(t, args) 165 case linux.TIOCSWINSZ: 166 return 0, rfd.inode.t.ld.setWindowSize(t, args) 167 case linux.TIOCSCTTY: 168 // Make the given terminal the controlling terminal of the 169 // calling process. 170 steal := args[2].Int() == 1 171 return 0, rfd.inode.t.setControllingTTY(ctx, steal, false /* isMaster */, rfd.vfsfd.IsReadable()) 172 case linux.TIOCNOTTY: 173 // Release this process's controlling terminal. 174 return 0, rfd.inode.t.releaseControllingTTY(ctx, false /* isMaster */) 175 case linux.TIOCGPGRP: 176 // Get the foreground process group. 177 return rfd.inode.t.foregroundProcessGroup(ctx, args, false /* isMaster */) 178 case linux.TIOCSPGRP: 179 // Set the foreground process group. 180 return rfd.inode.t.setForegroundProcessGroup(ctx, args, false /* isMaster */) 181 default: 182 maybeEmitUnimplementedEvent(ctx, cmd) 183 return 0, syserror.ENOTTY 184 } 185 } 186 187 // SetStat implements vfs.FileDescriptionImpl.SetStat. 188 func (rfd *replicaFileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error { 189 creds := auth.CredentialsFromContext(ctx) 190 fs := rfd.vfsfd.VirtualDentry().Mount().Filesystem() 191 return rfd.inode.SetStat(ctx, fs, creds, opts) 192 } 193 194 // Stat implements vfs.FileDescriptionImpl.Stat. 195 func (rfd *replicaFileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) { 196 fs := rfd.vfsfd.VirtualDentry().Mount().Filesystem() 197 return rfd.inode.Stat(ctx, fs, opts) 198 }