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