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  }