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  }