]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Fix bad recursive reads in piece resource chunks ReadAt
authorMatt Joiner <anacrolix@gmail.com>
Fri, 12 Jul 2024 05:21:37 +0000 (15:21 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Fri, 12 Jul 2024 05:21:37 +0000 (15:21 +1000)
It would always read from the first chunk, and scan across io.EOF until it hit the right chunk.

storage/piece-resource.go

index 51a8f0273b3302473d55b75a89ca506ba79b0a5a..9576fea5f77b5d76b2b757a553f84e241ed4de8a 100644 (file)
@@ -267,25 +267,42 @@ type chunk struct {
 
 type chunks []chunk
 
-func (me chunks) ReadAt(b []byte, off int64) (int, error) {
-       for {
-               if len(me) == 0 {
-                       return 0, io.EOF
-               }
-               if me[0].offset <= off {
-                       break
-               }
-               me = me[1:]
+func (me chunks) ReadAt(b []byte, off int64) (n int, err error) {
+       i := sort.Search(len(me), func(i int) bool {
+               return me[i].offset > off
+       }) - 1
+       if i == -1 {
+               err = io.EOF
+               return
        }
-       n, err := me[0].instance.ReadAt(b, off-me[0].offset)
-       if n == len(b) {
-               return n, nil
+       chunk := me[i]
+       // Go made me do this with it's bullshit named return values and := operator.
+again:
+       n1, err := chunk.instance.ReadAt(b, off-chunk.offset)
+       b = b[n1:]
+       n += n1
+       // Should we check here that we're not io.EOF or nil, per ReadAt's contract? That way we know we
+       // don't have an error anymore for the rest of the block.
+       if len(b) == 0 {
+               // err = nil, so we don't send io.EOF on chunk boundaries?
+               return
        }
-       if err == nil || err == io.EOF {
-               n_, err := me[1:].ReadAt(b[n:], off+int64(n))
-               return n + n_, err
+       off += int64(n1)
+       i++
+       if i >= len(me) {
+               if err == nil {
+                       err = io.EOF
+               }
+               return
+       }
+       chunk = me[i]
+       if chunk.offset > off {
+               if err == nil {
+                       err = io.ErrUnexpectedEOF
+               }
+               return
        }
-       return n, err
+       goto again
 }
 
 func (s piecePerResourcePiece) getChunks(dir string) (chunks chunks) {