Repository: dragonflyoss/Dragonfly2
Pull request #1346 (2022-05-31 06:28:33)

fix: find partial completed overflow
Signed-off-by: Jim Ma <majinjing3@gmail.com>

<!--- Provide a general summary of your changes in the Title above -->

## Description

For range like: bytes=1024-, func `computePiecePosition` will return a negative end due to overflow, we need check the content length first.

## Related Issue

<!--- This project only accepts pull requests related to open issues -->
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
<!--- Please link to the issue here: -->

## Motivation and Context

<!--- Why is this change required? What problem does it solve? -->

## Screenshots (if appropriate)

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation Update (if none of the other choices apply)

## Checklist

<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have read the **CONTRIBUTING** document.
- [ ] I have added tests to cover my changes.



Patch:
diff --git a/client/daemon/storage/local_storage.go b/client/daemon/storage/local_storage.go
index 861e809d..440c1ef4 100644
--- a/client/daemon/storage/local_storage.go
+++ b/client/daemon/storage/local_storage.go
@@ -605,7 +605,23 @@ func (t *localTaskStore) partialCompleted(rg *clientutil.Range) bool {
 	if t.ContentLength == -1 {
 		return false
 	}
-	start, end := computePiecePosition(t.ContentLength, rg, util.ComputePieceSize)
+
+	realRange := &clientutil.Range{
+		Start:  rg.Start,
+		Length: rg.Length,
+	}
+
+	// handle range like: bytes=1024-
+	if realRange.Start+realRange.Length > t.ContentLength {
+		realRange.Length = t.ContentLength - realRange.Start
+	}
+
+	start, end := computePiecePosition(t.ContentLength, realRange, util.ComputePieceSize)
+	// fix int overflow
+	if start < 0 || end < 0 {
+		t.Warnf("wrong start and end piece num, %d, %d", start, end)
+		return false
+	}
 	for i := start; i <= end; i++ {
 		if _, ok := t.Pieces[i]; !ok {
 			return false


Test patch:
diff --git a/client/daemon/storage/local_storage_test.go b/client/daemon/storage/local_storage_test.go
index 6119f20b..e1e24e0a 100644
--- a/client/daemon/storage/local_storage_test.go
+++ b/client/daemon/storage/local_storage_test.go
@@ -22,6 +22,7 @@ import (
 	"crypto/md5"
 	"encoding/hex"
 	"io"
+	"math"
 	"math/rand"
 	"os"
 	"path"
@@ -34,6 +35,7 @@ import (
 	"d7y.io/dragonfly/v2/client/config"
 	"d7y.io/dragonfly/v2/client/daemon/test"
 	logger "d7y.io/dragonfly/v2/internal/dflog"
+	"d7y.io/dragonfly/v2/internal/util"
 	"d7y.io/dragonfly/v2/pkg/rpc/base"
 	_ "d7y.io/dragonfly/v2/pkg/rpc/dfdaemon/server"
 	"d7y.io/dragonfly/v2/pkg/util/digestutils"
@@ -480,3 +482,61 @@ func Test_computePiecePosition(t *testing.T) {
 		})
 	}
 }
+
+func TestLocalTaskStore_partialCompleted(t *testing.T) {
+	var testCases = []struct {
+		name            string
+		ContentLength   int64
+		ReadyPieceCount int32
+		Range           clientutil.Range
+		Found           bool
+	}{
+		{
+			name:            "range bytes=x-y partial completed",
+			ContentLength:   1024,
+			ReadyPieceCount: 1,
+			Range: clientutil.Range{
+				Start:  1,
+				Length: 1023,
+			},
+			Found: true,
+		},
+		{
+			name:            "range bytes=x-y no partial completed",
+			ContentLength:   util.DefaultPieceSize * 10,
+			ReadyPieceCount: 1,
+			Range: clientutil.Range{
+				Start:  1,
+				Length: util.DefaultPieceSize * 2,
+			},
+			Found: false,
+		},
+		{
+			name:            "range bytes=x- no partial completed",
+			ContentLength:   util.DefaultPieceSizeLimit * 1,
+			ReadyPieceCount: 1,
+			Range: clientutil.Range{
+				Start:  1,
+				Length: math.MaxInt - 1,
+			},
+			Found: false,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			assert := testifyassert.New(t)
+			lts := &localTaskStore{
+				persistentMetadata: persistentMetadata{
+					ContentLength: tc.ContentLength,
+					Pieces:        map[int32]PieceMetadata{},
+				},
+			}
+			for i := int32(0); i < tc.ReadyPieceCount; i++ {
+				lts.Pieces[i] = PieceMetadata{}
+			}
+			ok := lts.partialCompleted(&tc.Range)
+			assert.Equal(tc.Found, ok)
+		})
+	}
+}