github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/hostfd/hostfd_unsafe.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 16 17 import ( 18 "io" 19 "unsafe" 20 21 "golang.org/x/sys/unix" 22 "github.com/nicocha30/gvisor-ligolo/pkg/log" 23 "github.com/nicocha30/gvisor-ligolo/pkg/safemem" 24 ) 25 26 const ( 27 sizeofIovec = unsafe.Sizeof(unix.Iovec{}) 28 sizeofMsghdr = unsafe.Sizeof(unix.Msghdr{}) 29 ) 30 31 // Preadv2 reads up to dsts.NumBytes() bytes from host file descriptor fd into 32 // dsts. offset and flags are interpreted as for preadv2(2). 33 // 34 // Preconditions: !dsts.IsEmpty(). 35 func Preadv2(fd int32, dsts safemem.BlockSeq, offset int64, flags uint32) (uint64, error) { 36 // No buffering is necessary regardless of safecopy; host syscalls will 37 // return EFAULT if appropriate, instead of raising SIGBUS. 38 var ( 39 n uintptr 40 e unix.Errno 41 ) 42 if flags == 0 && dsts.NumBlocks() == 1 { 43 // Use read() or pread() to avoid iovec allocation and copying. 44 dst := dsts.Head() 45 if offset == -1 { 46 n, _, e = unix.Syscall(unix.SYS_READ, uintptr(fd), dst.Addr(), uintptr(dst.Len())) 47 } else { 48 n, _, e = unix.Syscall6(unix.SYS_PREAD64, uintptr(fd), dst.Addr(), uintptr(dst.Len()), uintptr(offset), 0 /* pos_h */, 0 /* unused */) 49 } 50 } else { 51 iovs := safemem.IovecsFromBlockSeq(dsts) 52 if len(iovs) > MaxReadWriteIov { 53 log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), MaxReadWriteIov) 54 iovs = iovs[:MaxReadWriteIov] 55 } 56 n, _, e = unix.Syscall6(unix.SYS_PREADV2, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(offset), 0 /* pos_h */, uintptr(flags)) 57 } 58 if e != 0 { 59 return 0, e 60 } 61 if n == 0 { 62 return 0, io.EOF 63 } 64 return uint64(n), nil 65 } 66 67 // Pwritev2 writes up to srcs.NumBytes() from srcs into host file descriptor 68 // fd. offset and flags are interpreted as for pwritev2(2). 69 // 70 // Preconditions: !srcs.IsEmpty(). 71 func Pwritev2(fd int32, srcs safemem.BlockSeq, offset int64, flags uint32) (uint64, error) { 72 // No buffering is necessary regardless of safecopy; host syscalls will 73 // return EFAULT if appropriate, instead of raising SIGBUS. 74 var ( 75 n uintptr 76 e unix.Errno 77 ) 78 if flags == 0 && srcs.NumBlocks() == 1 { 79 // Use write() or pwrite() to avoid iovec allocation and copying. 80 src := srcs.Head() 81 if offset == -1 { 82 n, _, e = unix.Syscall(unix.SYS_WRITE, uintptr(fd), src.Addr(), uintptr(src.Len())) 83 } else { 84 n, _, e = unix.Syscall6(unix.SYS_PWRITE64, uintptr(fd), src.Addr(), uintptr(src.Len()), uintptr(offset), 0 /* pos_h */, 0 /* unused */) 85 } 86 } else { 87 iovs := safemem.IovecsFromBlockSeq(srcs) 88 if len(iovs) > MaxReadWriteIov { 89 log.Debugf("hostfd.Preadv2: truncating from %d iovecs to %d", len(iovs), MaxReadWriteIov) 90 iovs = iovs[:MaxReadWriteIov] 91 } 92 n, _, e = unix.Syscall6(unix.SYS_PWRITEV2, uintptr(fd), uintptr((unsafe.Pointer)(&iovs[0])), uintptr(len(iovs)), uintptr(offset), 0 /* pos_h */, uintptr(flags)) 93 } 94 if e != 0 { 95 return 0, e 96 } 97 return uint64(n), nil 98 }