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 }