github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/runsc/boot/portforward/portforward_fd_rw.go (about) 1 // Copyright 2022 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 portforward 16 17 import ( 18 "io" 19 "sync" 20 21 "github.com/metacubex/gvisor/pkg/context" 22 "github.com/metacubex/gvisor/pkg/errors/linuxerr" 23 "github.com/metacubex/gvisor/pkg/sentry/vfs" 24 "github.com/metacubex/gvisor/pkg/usermem" 25 "github.com/metacubex/gvisor/pkg/waiter" 26 ) 27 28 // fileDescriptionConn 29 type fileDescriptionConn struct { 30 // file is the file to read and write from. 31 file *vfs.FileDescription 32 // once makes sure we release the owned FileDescription once. 33 once sync.Once 34 } 35 36 // NewFileDescriptionConn initializes a fileDescriptionConn. 37 func NewFileDescriptionConn(file *vfs.FileDescription) proxyConn { 38 return &fileDescriptionConn{file: file} 39 } 40 41 // Name implements proxyConn.Name. 42 func (r *fileDescriptionConn) Name() string { 43 return "fileDescriptionConn" 44 } 45 46 // Read implements proxyConn.Read. 47 func (r *fileDescriptionConn) Read(ctx context.Context, buf []byte, cancel <-chan struct{}) (int, error) { 48 var ( 49 notifyCh chan struct{} 50 waitEntry waiter.Entry 51 ) 52 n, err := r.file.Read(ctx, usermem.BytesIOSequence(buf), vfs.ReadOptions{}) 53 for linuxerr.Equals(linuxerr.ErrWouldBlock, err) { 54 if notifyCh == nil { 55 waitEntry, notifyCh = waiter.NewChannelEntry(waiter.ReadableEvents | waiter.WritableEvents | waiter.EventHUp | waiter.EventErr) 56 // Register for when the endpoint is readable or disconnected. 57 r.file.EventRegister(&waitEntry) 58 defer r.file.EventUnregister(&waitEntry) 59 } 60 select { 61 case <-notifyCh: 62 case <-cancel: 63 return 0, io.EOF 64 } 65 n, err = r.file.Read(ctx, usermem.BytesIOSequence(buf), vfs.ReadOptions{}) 66 } 67 68 // host fd FileDescriptions use recvmsg which returns zero when the 69 // peer has shutdown. When that happens return EOF. 70 if n == 0 && err == nil { 71 return 0, io.EOF 72 } 73 return int(n), err 74 } 75 76 // Write implements proxyConn.Write. 77 func (r *fileDescriptionConn) Write(ctx context.Context, buf []byte, cancel <-chan struct{}) (int, error) { 78 var notifyCh chan struct{} 79 var waitEntry waiter.Entry 80 n, err := r.file.Write(ctx, usermem.BytesIOSequence(buf), vfs.WriteOptions{}) 81 for linuxerr.Equals(linuxerr.ErrWouldBlock, err) { 82 if notifyCh == nil { 83 waitEntry, notifyCh = waiter.NewChannelEntry(waiter.WritableEvents | waiter.EventHUp | waiter.EventErr) 84 // Register for when the endpoint is writable or disconnected. 85 r.file.EventRegister(&waitEntry) 86 defer r.file.EventUnregister(&waitEntry) 87 } 88 select { 89 case <-notifyCh: 90 case <-cancel: 91 return 0, io.EOF 92 } 93 n, err = r.file.Write(ctx, usermem.BytesIOSequence(buf), vfs.WriteOptions{}) 94 } 95 return int(n), err 96 } 97 98 // Close implements proxyConn.Close. 99 func (r *fileDescriptionConn) Close(ctx context.Context) { 100 r.once.Do(func() { 101 r.file.DecRef(ctx) 102 }) 103 }