diff --git a/backend/cache/object.go b/backend/cache/object.go
index 87395db8a..c17b458fb 100644
--- a/backend/cache/object.go
+++ b/backend/cache/object.go
@@ -220,11 +220,11 @@ func (o *Object) Open(options ...fs.OpenOption) (io.ReadCloser, error) {
 
 	var err error
 	cacheReader := NewObjectHandle(o)
-	var offset, limit int64
+	var offset, limit int64 = 0, -1
 	for _, option := range options {
 		switch x := option.(type) {
 		case *fs.SeekOption:
-			offset, limit = x.Offset, 0
+			offset = x.Offset
 		case *fs.RangeOption:
 			offset, limit = x.Decode(o.Size())
 		}
diff --git a/backend/ftp/ftp.go b/backend/ftp/ftp.go
index 7f333550d..683a2963a 100644
--- a/backend/ftp/ftp.go
+++ b/backend/ftp/ftp.go
@@ -634,11 +634,11 @@ func (f *ftpReadCloser) Close() error {
 func (o *Object) Open(options ...fs.OpenOption) (rc io.ReadCloser, err error) {
 	// defer fs.Trace(o, "")("rc=%v, err=%v", &rc, &err)
 	path := path.Join(o.fs.root, o.remote)
-	var offset, limit int64
+	var offset, limit int64 = 0, -1
 	for _, option := range options {
 		switch x := option.(type) {
 		case *fs.SeekOption:
-			offset, limit = x.Offset, 0
+			offset = x.Offset
 		case *fs.RangeOption:
 			offset, limit = x.Decode(o.Size())
 		default:
diff --git a/backend/local/local.go b/backend/local/local.go
index 55362a5c2..573ffa0ab 100644
--- a/backend/local/local.go
+++ b/backend/local/local.go
@@ -676,13 +676,12 @@ func (file *localOpenFile) Close() (err error) {
 
 // Open an object for read
 func (o *Object) Open(options ...fs.OpenOption) (in io.ReadCloser, err error) {
-	var offset int64
-	var limit int64
+	var offset, limit int64 = 0, -1
 	hashes := hash.Supported
 	for _, option := range options {
 		switch x := option.(type) {
 		case *fs.SeekOption:
-			offset, limit = x.Offset, 0
+			offset = x.Offset
 		case *fs.RangeOption:
 			offset, limit = x.Decode(o.size)
 		case *fs.HashesOption:
diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go
index a885c1267..4b3c9dec5 100644
--- a/backend/sftp/sftp.go
+++ b/backend/sftp/sftp.go
@@ -855,11 +855,11 @@ func (file *ObjectReader) Close() (err error) {
 
 // Open a remote sftp file object for reading. Seek is supported
 func (o *Object) Open(options ...fs.OpenOption) (in io.ReadCloser, err error) {
-	var offset, limit int64
+	var offset, limit int64 = 0, -1
 	for _, option := range options {
 		switch x := option.(type) {
 		case *fs.SeekOption:
-			offset, limit = x.Offset, 0
+			offset = x.Offset
 		case *fs.RangeOption:
 			offset, limit = x.Decode(o.Size())
 		default:
diff --git a/fs/options.go b/fs/options.go
index 66345466d..9eae1d24d 100644
--- a/fs/options.go
+++ b/fs/options.go
@@ -97,17 +97,25 @@ func (o *RangeOption) Mandatory() bool {
 }
 
 // Decode interprets the RangeOption into an offset and a limit
+//
+// The offset is the start of the stream and the limit is how many
+// bytes should be read from it.  If the limit is -1 then the stream
+// should be read to the end.
 func (o *RangeOption) Decode(size int64) (offset, limit int64) {
 	if o.Start >= 0 {
 		offset = o.Start
 		if o.End >= 0 {
 			limit = o.End - o.Start + 1
 		} else {
-			limit = 0
+			limit = -1
 		}
 	} else {
-		offset = size - o.End
-		limit = 0
+		if o.End >= 0 {
+			offset = size - o.End
+		} else {
+			offset = 0
+		}
+		limit = -1
 	}
 	return offset, limit
 }
diff --git a/fs/options_test.go b/fs/options_test.go
index 70ad2883a..bad992390 100644
--- a/fs/options_test.go
+++ b/fs/options_test.go
@@ -37,3 +37,24 @@ func TestParseRangeOption(t *testing.T) {
 		}
 	}
 }
+
+func TestRangeOptionDecode(t *testing.T) {
+	for _, test := range []struct {
+		in         RangeOption
+		size       int64
+		wantOffset int64
+		wantLimit  int64
+	}{
+		{in: RangeOption{Start: 1, End: 10}, size: 100, wantOffset: 1, wantLimit: 10},
+		{in: RangeOption{Start: 10, End: 10}, size: 100, wantOffset: 10, wantLimit: 1},
+		{in: RangeOption{Start: 10, End: 9}, size: 100, wantOffset: 10, wantLimit: 0},
+		{in: RangeOption{Start: 1, End: -1}, size: 100, wantOffset: 1, wantLimit: -1},
+		{in: RangeOption{Start: -1, End: 90}, size: 100, wantOffset: 10, wantLimit: -1},
+		{in: RangeOption{Start: -1, End: -1}, size: 100, wantOffset: 0, wantLimit: -1},
+	} {
+		gotOffset, gotLimit := test.in.Decode(test.size)
+		what := fmt.Sprintf("%+v size=%d", test.in, test.size)
+		assert.Equal(t, test.wantOffset, gotOffset, "offset "+what)
+		assert.Equal(t, test.wantLimit, gotLimit, "limit "+what)
+	}
+}
diff --git a/lib/readers/limited.go b/lib/readers/limited.go
index ad28763ed..218dd661a 100644
--- a/lib/readers/limited.go
+++ b/lib/readers/limited.go
@@ -9,10 +9,10 @@ type LimitedReadCloser struct {
 }
 
 // NewLimitedReadCloser returns a LimitedReadCloser wrapping rc to
-// limit it to reading limit bytes. If limit == 0 then it does not
+// limit it to reading limit bytes. If limit < 0 then it does not
 // wrap rc, it just returns it.
 func NewLimitedReadCloser(rc io.ReadCloser, limit int64) (lrc io.ReadCloser) {
-	if limit == 0 {
+	if limit < 0 {
 		return rc
 	}
 	return &LimitedReadCloser{