github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/pipe/node.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 pipe
    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/sentry/fs"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil"
    23  	"github.com/SagerNet/gvisor/pkg/sync"
    24  	"github.com/SagerNet/gvisor/pkg/syserror"
    25  )
    26  
    27  // inodeOperations implements fs.InodeOperations for pipes.
    28  //
    29  // +stateify savable
    30  type inodeOperations struct {
    31  	fsutil.InodeGenericChecker       `state:"nosave"`
    32  	fsutil.InodeNoExtendedAttributes `state:"nosave"`
    33  	fsutil.InodeNoopRelease          `state:"nosave"`
    34  	fsutil.InodeNoopTruncate         `state:"nosave"`
    35  	fsutil.InodeNoopWriteOut         `state:"nosave"`
    36  	fsutil.InodeNotDirectory         `state:"nosave"`
    37  	fsutil.InodeNotMappable          `state:"nosave"`
    38  	fsutil.InodeNotSocket            `state:"nosave"`
    39  	fsutil.InodeNotSymlink           `state:"nosave"`
    40  
    41  	// Marking pipe inodes as virtual allows them to be saved and restored
    42  	// even if they have been unlinked. We can get away with this because
    43  	// their state exists entirely within the sentry.
    44  	fsutil.InodeVirtual `state:"nosave"`
    45  
    46  	fsutil.InodeSimpleAttributes
    47  
    48  	// mu protects the fields below.
    49  	mu sync.Mutex `state:"nosave"`
    50  
    51  	// p is the underlying Pipe object representing this fifo.
    52  	p *Pipe
    53  
    54  	// Channels for synchronizing the creation of new readers and writers of
    55  	// this fifo. See waitFor and newHandleLocked.
    56  	//
    57  	// These are not saved/restored because all waiters are unblocked on save,
    58  	// and either automatically restart (via ERESTARTSYS) or return EINTR on
    59  	// resume. On restarts via ERESTARTSYS, the appropriate channel will be
    60  	// recreated.
    61  	rWakeup chan struct{} `state:"nosave"`
    62  	wWakeup chan struct{} `state:"nosave"`
    63  }
    64  
    65  var _ fs.InodeOperations = (*inodeOperations)(nil)
    66  
    67  // NewInodeOperations returns a new fs.InodeOperations for a given pipe.
    68  func NewInodeOperations(ctx context.Context, perms fs.FilePermissions, p *Pipe) *inodeOperations {
    69  	return &inodeOperations{
    70  		InodeSimpleAttributes: fsutil.NewInodeSimpleAttributes(ctx, fs.FileOwnerFromContext(ctx), perms, linux.PIPEFS_MAGIC),
    71  		p:                     p,
    72  	}
    73  }
    74  
    75  // GetFile implements fs.InodeOperations.GetFile. Named pipes have special blocking
    76  // semantics during open:
    77  //
    78  // "Normally, opening the FIFO blocks until the other end is opened also. A
    79  // process can open a FIFO in nonblocking mode. In this case, opening for
    80  // read-only will succeed even if no-one has opened on the write side yet,
    81  // opening for write-only will fail with ENXIO (no such device or address)
    82  // unless the other end has already been opened. Under Linux, opening a FIFO
    83  // for read and write will succeed both in blocking and nonblocking mode. POSIX
    84  // leaves this behavior undefined. This can be used to open a FIFO for writing
    85  // while there are no readers available." - fifo(7)
    86  func (i *inodeOperations) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags) (*fs.File, error) {
    87  	i.mu.Lock()
    88  	defer i.mu.Unlock()
    89  
    90  	switch {
    91  	case flags.Read && !flags.Write: // O_RDONLY.
    92  		r := i.p.Open(ctx, d, flags)
    93  		newHandleLocked(&i.rWakeup)
    94  
    95  		if i.p.isNamed && !flags.NonBlocking && !i.p.HasWriters() {
    96  			if !waitFor(&i.mu, &i.wWakeup, ctx) {
    97  				r.DecRef(ctx)
    98  				return nil, syserror.ErrInterrupted
    99  			}
   100  		}
   101  
   102  		// By now, either we're doing a nonblocking open or we have a writer. On
   103  		// a nonblocking read-only open, the open succeeds even if no-one has
   104  		// opened the write side yet.
   105  		return r, nil
   106  
   107  	case flags.Write && !flags.Read: // O_WRONLY.
   108  		w := i.p.Open(ctx, d, flags)
   109  		newHandleLocked(&i.wWakeup)
   110  
   111  		if i.p.isNamed && !i.p.HasReaders() {
   112  			// On a nonblocking, write-only open, the open fails with ENXIO if the
   113  			// read side isn't open yet.
   114  			if flags.NonBlocking {
   115  				w.DecRef(ctx)
   116  				return nil, linuxerr.ENXIO
   117  			}
   118  
   119  			if !waitFor(&i.mu, &i.rWakeup, ctx) {
   120  				w.DecRef(ctx)
   121  				return nil, syserror.ErrInterrupted
   122  			}
   123  		}
   124  		return w, nil
   125  
   126  	case flags.Read && flags.Write: // O_RDWR.
   127  		// Pipes opened for read-write always succeeds without blocking.
   128  		rw := i.p.Open(ctx, d, flags)
   129  		newHandleLocked(&i.rWakeup)
   130  		newHandleLocked(&i.wWakeup)
   131  		return rw, nil
   132  
   133  	default:
   134  		return nil, linuxerr.EINVAL
   135  	}
   136  }
   137  
   138  func (*inodeOperations) Allocate(_ context.Context, _ *fs.Inode, _, _ int64) error {
   139  	return linuxerr.EPIPE
   140  }