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)