github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/gofer/handle.go (about) 1 // Copyright 2019 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 gofer 16 17 import ( 18 "golang.org/x/sys/unix" 19 "github.com/SagerNet/gvisor/pkg/context" 20 "github.com/SagerNet/gvisor/pkg/p9" 21 "github.com/SagerNet/gvisor/pkg/safemem" 22 "github.com/SagerNet/gvisor/pkg/sentry/hostfd" 23 ) 24 25 // handle represents a remote "open file descriptor", consisting of an opened 26 // fid (p9.File) and optionally a host file descriptor. 27 // 28 // These are explicitly not savable. 29 type handle struct { 30 file p9file 31 fd int32 // -1 if unavailable 32 } 33 34 // Preconditions: read || write. 35 func openHandle(ctx context.Context, file p9file, read, write, trunc bool) (handle, error) { 36 _, newfile, err := file.walk(ctx, nil) 37 if err != nil { 38 return handle{fd: -1}, err 39 } 40 var flags p9.OpenFlags 41 switch { 42 case read && !write: 43 flags = p9.ReadOnly 44 case !read && write: 45 flags = p9.WriteOnly 46 case read && write: 47 flags = p9.ReadWrite 48 } 49 if trunc { 50 flags |= p9.OpenTruncate 51 } 52 fdobj, _, _, err := newfile.open(ctx, flags) 53 if err != nil { 54 newfile.close(ctx) 55 return handle{fd: -1}, err 56 } 57 fd := int32(-1) 58 if fdobj != nil { 59 fd = int32(fdobj.Release()) 60 } 61 return handle{ 62 file: newfile, 63 fd: fd, 64 }, nil 65 } 66 67 func (h *handle) isOpen() bool { 68 return !h.file.isNil() 69 } 70 71 func (h *handle) close(ctx context.Context) { 72 h.file.close(ctx) 73 h.file = p9file{} 74 if h.fd >= 0 { 75 unix.Close(int(h.fd)) 76 h.fd = -1 77 } 78 } 79 80 func (h *handle) readToBlocksAt(ctx context.Context, dsts safemem.BlockSeq, offset uint64) (uint64, error) { 81 if dsts.IsEmpty() { 82 return 0, nil 83 } 84 if h.fd >= 0 { 85 ctx.UninterruptibleSleepStart(false) 86 n, err := hostfd.Preadv2(h.fd, dsts, int64(offset), 0 /* flags */) 87 ctx.UninterruptibleSleepFinish(false) 88 return n, err 89 } 90 if dsts.NumBlocks() == 1 && !dsts.Head().NeedSafecopy() { 91 n, err := h.file.readAt(ctx, dsts.Head().ToSlice(), offset) 92 return uint64(n), err 93 } 94 // Buffer the read since p9.File.ReadAt() takes []byte. 95 buf := make([]byte, dsts.NumBytes()) 96 n, err := h.file.readAt(ctx, buf, offset) 97 if n == 0 { 98 return 0, err 99 } 100 if cp, cperr := safemem.CopySeq(dsts, safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf[:n]))); cperr != nil { 101 return cp, cperr 102 } 103 return uint64(n), err 104 } 105 106 func (h *handle) writeFromBlocksAt(ctx context.Context, srcs safemem.BlockSeq, offset uint64) (uint64, error) { 107 if srcs.IsEmpty() { 108 return 0, nil 109 } 110 if h.fd >= 0 { 111 ctx.UninterruptibleSleepStart(false) 112 n, err := hostfd.Pwritev2(h.fd, srcs, int64(offset), 0 /* flags */) 113 ctx.UninterruptibleSleepFinish(false) 114 return n, err 115 } 116 if srcs.NumBlocks() == 1 && !srcs.Head().NeedSafecopy() { 117 n, err := h.file.writeAt(ctx, srcs.Head().ToSlice(), offset) 118 return uint64(n), err 119 } 120 // Buffer the write since p9.File.WriteAt() takes []byte. 121 buf := make([]byte, srcs.NumBytes()) 122 cp, cperr := safemem.CopySeq(safemem.BlockSeqOf(safemem.BlockFromSafeSlice(buf)), srcs) 123 if cp == 0 { 124 return 0, cperr 125 } 126 n, err := h.file.writeAt(ctx, buf[:cp], offset) 127 // err takes precedence over cperr. 128 if err != nil { 129 return uint64(n), err 130 } 131 return uint64(n), cperr 132 }