gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/secio/secio.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 secio provides support for sectioned I/O. 16 package secio 17 18 import ( 19 "errors" 20 "io" 21 ) 22 23 // ErrReachedLimit is returned when SectionReader.Read or SectionWriter.Write 24 // reaches its limit. 25 var ErrReachedLimit = errors.New("reached limit") 26 27 // SectionReader implements io.Reader on a section of an underlying io.ReaderAt. 28 // It is similar to io.SectionReader, but: 29 // 30 // - Reading beyond the limit returns ErrReachedLimit, not io.EOF. 31 // 32 // - Limit overflow is handled correctly. 33 type SectionReader struct { 34 r io.ReaderAt 35 off int64 36 limit int64 37 } 38 39 // Read implements io.Reader.Read. 40 func (r *SectionReader) Read(dst []byte) (int, error) { 41 if r.limit >= 0 { 42 if max := r.limit - r.off; max < int64(len(dst)) { 43 dst = dst[:max] 44 } 45 } 46 n, err := r.r.ReadAt(dst, r.off) 47 r.off += int64(n) 48 if err == nil && r.off == r.limit { 49 err = ErrReachedLimit 50 } 51 return n, err 52 } 53 54 // NewOffsetReader returns an io.Reader that reads from r starting at offset 55 // off. 56 func NewOffsetReader(r io.ReaderAt, off int64) *SectionReader { 57 return &SectionReader{r, off, -1} 58 } 59 60 // NewSectionReader returns an io.Reader that reads from r starting at offset 61 // off and stops with ErrReachedLimit after n bytes. 62 func NewSectionReader(r io.ReaderAt, off int64, n int64) *SectionReader { 63 // If off + n overflows, it will be < 0 such that no limit applies, but 64 // this is the correct behavior as long as r prohibits reading at offsets 65 // beyond MaxInt64. 66 return &SectionReader{r, off, off + n} 67 } 68 69 // SectionWriter implements io.Writer on a section of an underlying 70 // io.WriterAt. Writing beyond the limit returns ErrReachedLimit. 71 type SectionWriter struct { 72 w io.WriterAt 73 off int64 74 limit int64 75 } 76 77 // Write implements io.Writer.Write. 78 func (w *SectionWriter) Write(src []byte) (int, error) { 79 if w.limit >= 0 { 80 if max := w.limit - w.off; max < int64(len(src)) { 81 src = src[:max] 82 } 83 } 84 n, err := w.w.WriteAt(src, w.off) 85 w.off += int64(n) 86 if err == nil && w.off == w.limit { 87 err = ErrReachedLimit 88 } 89 return n, err 90 } 91 92 // NewOffsetWriter returns an io.Writer that writes to w starting at offset 93 // off. 94 func NewOffsetWriter(w io.WriterAt, off int64) *SectionWriter { 95 return &SectionWriter{w, off, -1} 96 } 97 98 // NewSectionWriter returns an io.Writer that writes to w starting at offset 99 // off and stops with ErrReachedLimit after n bytes. 100 func NewSectionWriter(w io.WriterAt, off int64, n int64) *SectionWriter { 101 // If off + n overflows, it will be < 0 such that no limit applies, but 102 // this is the correct behavior as long as w prohibits writing at offsets 103 // beyond MaxInt64. 104 return &SectionWriter{w, off, off + n} 105 }