github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fs/fdpipe/pipe.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 fdpipe implements common namedpipe opening and accessing logic. 16 package fdpipe 17 18 import ( 19 "os" 20 21 "golang.org/x/sys/unix" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 24 "github.com/SagerNet/gvisor/pkg/fd" 25 "github.com/SagerNet/gvisor/pkg/fdnotifier" 26 "github.com/SagerNet/gvisor/pkg/log" 27 "github.com/SagerNet/gvisor/pkg/safemem" 28 "github.com/SagerNet/gvisor/pkg/secio" 29 "github.com/SagerNet/gvisor/pkg/sentry/fs" 30 "github.com/SagerNet/gvisor/pkg/sentry/fs/fsutil" 31 "github.com/SagerNet/gvisor/pkg/sync" 32 "github.com/SagerNet/gvisor/pkg/syserror" 33 "github.com/SagerNet/gvisor/pkg/usermem" 34 "github.com/SagerNet/gvisor/pkg/waiter" 35 ) 36 37 // pipeOperations are the fs.FileOperations of a host pipe. 38 // 39 // +stateify savable 40 type pipeOperations struct { 41 fsutil.FilePipeSeek `state:"nosave"` 42 fsutil.FileNotDirReaddir `state:"nosave"` 43 fsutil.FileNoFsync `state:"nosave"` 44 fsutil.FileNoopFlush `state:"nosave"` 45 fsutil.FileNoMMap `state:"nosave"` 46 fsutil.FileNoIoctl `state:"nosave"` 47 fsutil.FileNoSplice `state:"nosave"` 48 fsutil.FileUseInodeUnstableAttr `state:"nosave"` 49 waiter.Queue `state:"nosave"` 50 51 // flags are the flags used to open the pipe. 52 flags fs.FileFlags `state:".(fs.FileFlags)"` 53 54 // opener is how the pipe was opened. 55 opener NonBlockingOpener `state:"wait"` 56 57 // file represents the host pipe. 58 file *fd.FD `state:"nosave"` 59 60 // mu protects readAheadBuffer access below. 61 mu sync.Mutex `state:"nosave"` 62 63 // readAheadBuffer contains read bytes that have not yet been read 64 // by the application but need to be buffered for save-restore for correct 65 // opening semantics. The readAheadBuffer will only be non-empty when the 66 // is first opened and will be drained by subsequent reads on the pipe. 67 readAheadBuffer []byte 68 } 69 70 // newPipeOperations returns an implementation of fs.FileOperations for a pipe. 71 func newPipeOperations(ctx context.Context, opener NonBlockingOpener, flags fs.FileFlags, file *fd.FD, readAheadBuffer []byte) (*pipeOperations, error) { 72 pipeOps := &pipeOperations{ 73 flags: flags, 74 opener: opener, 75 file: file, 76 readAheadBuffer: readAheadBuffer, 77 } 78 if err := pipeOps.init(); err != nil { 79 return nil, err 80 } 81 return pipeOps, nil 82 } 83 84 // init initializes p.file. 85 func (p *pipeOperations) init() error { 86 var s unix.Stat_t 87 if err := unix.Fstat(p.file.FD(), &s); err != nil { 88 log.Warningf("pipe: cannot stat fd %d: %v", p.file.FD(), err) 89 return unix.EINVAL 90 } 91 if (s.Mode & unix.S_IFMT) != unix.S_IFIFO { 92 log.Warningf("pipe: cannot load fd %d as pipe, file type: %o", p.file.FD(), s.Mode) 93 return unix.EINVAL 94 } 95 if err := unix.SetNonblock(p.file.FD(), true); err != nil { 96 return err 97 } 98 return fdnotifier.AddFD(int32(p.file.FD()), &p.Queue) 99 } 100 101 // EventRegister implements waiter.Waitable.EventRegister. 102 func (p *pipeOperations) EventRegister(e *waiter.Entry, mask waiter.EventMask) { 103 p.Queue.EventRegister(e, mask) 104 fdnotifier.UpdateFD(int32(p.file.FD())) 105 } 106 107 // EventUnregister implements waiter.Waitable.EventUnregister. 108 func (p *pipeOperations) EventUnregister(e *waiter.Entry) { 109 p.Queue.EventUnregister(e) 110 fdnotifier.UpdateFD(int32(p.file.FD())) 111 } 112 113 // Readiness returns a mask of ready events for stream. 114 func (p *pipeOperations) Readiness(mask waiter.EventMask) (eventMask waiter.EventMask) { 115 return fdnotifier.NonBlockingPoll(int32(p.file.FD()), mask) 116 } 117 118 // Release implements fs.FileOperations.Release. 119 func (p *pipeOperations) Release(context.Context) { 120 fdnotifier.RemoveFD(int32(p.file.FD())) 121 p.file.Close() 122 p.file = nil 123 } 124 125 // Read implements fs.FileOperations.Read. 126 func (p *pipeOperations) Read(ctx context.Context, file *fs.File, dst usermem.IOSequence, offset int64) (int64, error) { 127 // Drain the read ahead buffer, if it contains anything first. 128 var bufN int 129 var bufErr error 130 p.mu.Lock() 131 if len(p.readAheadBuffer) > 0 { 132 bufN, bufErr = dst.CopyOut(ctx, p.readAheadBuffer) 133 p.readAheadBuffer = p.readAheadBuffer[bufN:] 134 dst = dst.DropFirst(bufN) 135 } 136 p.mu.Unlock() 137 if dst.NumBytes() == 0 || bufErr != nil { 138 return int64(bufN), bufErr 139 } 140 141 // Pipes expect full reads. 142 n, err := dst.CopyOutFrom(ctx, safemem.FromIOReader{secio.FullReader{p.file}}) 143 total := int64(bufN) + n 144 if err != nil && isBlockError(err) { 145 return total, syserror.ErrWouldBlock 146 } 147 return total, err 148 } 149 150 // Write implements fs.FileOperations.Write. 151 func (p *pipeOperations) Write(ctx context.Context, file *fs.File, src usermem.IOSequence, offset int64) (int64, error) { 152 n, err := src.CopyInTo(ctx, safemem.FromIOWriter{p.file}) 153 if err != nil && isBlockError(err) { 154 return n, syserror.ErrWouldBlock 155 } 156 return n, err 157 } 158 159 // isBlockError unwraps os errors and checks if they are caused by EAGAIN or 160 // EWOULDBLOCK. This is so they can be transformed into syserror.ErrWouldBlock. 161 func isBlockError(err error) bool { 162 if linuxerr.Equals(linuxerr.EAGAIN, err) || linuxerr.Equals(linuxerr.EWOULDBLOCK, err) { 163 return true 164 } 165 if pe, ok := err.(*os.PathError); ok { 166 return isBlockError(pe.Err) 167 } 168 return false 169 }