github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/hostfd/hostfd.go (about) 1 // Copyright 2020 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 hostfd provides efficient I/O with host file descriptors. 16 package hostfd 17 18 import ( 19 "io" 20 21 "golang.org/x/sys/unix" 22 "github.com/metacubex/gvisor/pkg/safemem" 23 "github.com/metacubex/gvisor/pkg/sync" 24 ) 25 26 // ReadWriterAt implements safemem.Reader and safemem.Writer by reading from 27 // and writing to a host file descriptor respectively. ReadWriterAts should be 28 // obtained by calling GetReadWriterAt. 29 // 30 // Clients should usually prefer to use Preadv2 and Pwritev2 directly. 31 type ReadWriterAt struct { 32 fd int32 33 offset int64 34 flags uint32 35 } 36 37 var rwpool = sync.Pool{ 38 New: func() any { 39 return &ReadWriterAt{} 40 }, 41 } 42 43 // GetReadWriterAt returns a ReadWriterAt that reads from / writes to the given 44 // host file descriptor, starting at the given offset and using the given 45 // preadv2(2)/pwritev2(2) flags. If offset is -1, the host file descriptor's 46 // offset is used instead. Users are responsible for ensuring that fd remains 47 // valid for the lifetime of the returned ReadWriterAt, and must call 48 // PutReadWriterAt when it is no longer needed. 49 func GetReadWriterAt(fd int32, offset int64, flags uint32) *ReadWriterAt { 50 rw := rwpool.Get().(*ReadWriterAt) 51 *rw = ReadWriterAt{ 52 fd: fd, 53 offset: offset, 54 flags: flags, 55 } 56 return rw 57 } 58 59 // PutReadWriterAt releases a ReadWriterAt returned by a previous call to 60 // GetReadWriterAt that is no longer in use. 61 func PutReadWriterAt(rw *ReadWriterAt) { 62 rwpool.Put(rw) 63 } 64 65 // ReadToBlocks implements safemem.Reader.ReadToBlocks. 66 func (rw *ReadWriterAt) ReadToBlocks(dsts safemem.BlockSeq) (uint64, error) { 67 if dsts.IsEmpty() { 68 return 0, nil 69 } 70 n, err := Preadv2(rw.fd, dsts, rw.offset, rw.flags) 71 if rw.offset >= 0 { 72 rw.offset += int64(n) 73 } 74 return n, err 75 } 76 77 // WriteFromBlocks implements safemem.Writer.WriteFromBlocks. 78 func (rw *ReadWriterAt) WriteFromBlocks(srcs safemem.BlockSeq) (uint64, error) { 79 if srcs.IsEmpty() { 80 return 0, nil 81 } 82 n, err := Pwritev2(rw.fd, srcs, rw.offset, rw.flags) 83 if rw.offset >= 0 { 84 rw.offset += int64(n) 85 } 86 return n, err 87 } 88 89 // Preadv2 reads up to dsts.NumBytes() bytes from host file descriptor fd into 90 // dsts. offset and flags are interpreted as for preadv2(2). 91 // 92 // Preconditions: !dsts.IsEmpty(). 93 func Preadv2(fd int32, dsts safemem.BlockSeq, offset int64, flags uint32) (uint64, error) { 94 // No buffering is necessary regardless of safecopy; host syscalls will 95 // return EFAULT if appropriate, instead of raising SIGBUS. 96 var ( 97 n uintptr 98 e unix.Errno 99 ) 100 if flags == 0 && dsts.NumBlocks() == 1 { 101 // Use read() or pread() to avoid iovec allocation and copying. 102 dst := dsts.Head() 103 if offset == -1 { 104 n, _, e = unix.Syscall(unix.SYS_READ, uintptr(fd), dst.Addr(), uintptr(dst.Len())) 105 } else { 106 n, _, e = unix.Syscall6(unix.SYS_PREAD64, uintptr(fd), dst.Addr(), uintptr(dst.Len()), uintptr(offset), 0 /* pos_h */, 0 /* unused */) 107 } 108 } else { 109 n, e = iovecsReadWrite(unix.SYS_PREADV2, fd, safemem.IovecsFromBlockSeq(dsts), offset, flags) 110 } 111 if e != 0 { 112 return 0, e 113 } 114 if n == 0 { 115 return 0, io.EOF 116 } 117 return uint64(n), nil 118 } 119 120 // Pwritev2 writes up to srcs.NumBytes() from srcs into host file descriptor 121 // fd. offset and flags are interpreted as for pwritev2(2). 122 // 123 // Preconditions: !srcs.IsEmpty(). 124 func Pwritev2(fd int32, srcs safemem.BlockSeq, offset int64, flags uint32) (uint64, error) { 125 // No buffering is necessary regardless of safecopy; host syscalls will 126 // return EFAULT if appropriate, instead of raising SIGBUS. 127 var ( 128 n uintptr 129 e unix.Errno 130 ) 131 if flags == 0 && srcs.NumBlocks() == 1 { 132 // Use write() or pwrite() to avoid iovec allocation and copying. 133 src := srcs.Head() 134 if offset == -1 { 135 n, _, e = unix.Syscall(unix.SYS_WRITE, uintptr(fd), src.Addr(), uintptr(src.Len())) 136 } else { 137 n, _, e = unix.Syscall6(unix.SYS_PWRITE64, uintptr(fd), src.Addr(), uintptr(src.Len()), uintptr(offset), 0 /* pos_h */, 0 /* unused */) 138 } 139 } else { 140 n, e = iovecsReadWrite(unix.SYS_PWRITEV2, fd, safemem.IovecsFromBlockSeq(srcs), offset, flags) 141 } 142 if e != 0 { 143 return 0, e 144 } 145 return uint64(n), nil 146 }