mirror of https://github.com/rclone/rclone.git
fs/object: fix memory object out of bounds Seek
This commit is contained in:
parent
64b3d1d539
commit
ccef29bbff
|
@ -279,19 +279,26 @@ func (o *MemoryObject) SetModTime(ctx context.Context, modTime time.Time) error
|
||||||
|
|
||||||
// Open opens the file for read. Call Close() on the returned io.ReadCloser
|
// Open opens the file for read. Call Close() on the returned io.ReadCloser
|
||||||
func (o *MemoryObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
|
func (o *MemoryObject) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadCloser, error) {
|
||||||
content := o.content
|
var offset, limit int64 = 0, -1
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
switch x := option.(type) {
|
switch x := option.(type) {
|
||||||
case *fs.RangeOption:
|
case *fs.RangeOption:
|
||||||
content = o.content[x.Start:x.End]
|
offset, limit = x.Decode(o.Size())
|
||||||
case *fs.SeekOption:
|
case *fs.SeekOption:
|
||||||
content = o.content[x.Offset:]
|
offset = x.Offset
|
||||||
default:
|
default:
|
||||||
if option.Mandatory() {
|
if option.Mandatory() {
|
||||||
fs.Logf(o, "Unsupported mandatory option: %v", option)
|
fs.Logf(o, "Unsupported mandatory option: %v", option)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
content := o.content
|
||||||
|
offset = max(offset, 0)
|
||||||
|
if limit < 0 {
|
||||||
|
content = content[offset:]
|
||||||
|
} else {
|
||||||
|
content = content[offset:min(offset+limit, int64(len(content)))]
|
||||||
|
}
|
||||||
return io.NopCloser(bytes.NewBuffer(content)), nil
|
return io.NopCloser(bytes.NewBuffer(content)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,7 @@ func TestMemoryObject(t *testing.T) {
|
||||||
assert.Equal(t, newNow, o.ModTime(context.Background()))
|
assert.Equal(t, newNow, o.ModTime(context.Background()))
|
||||||
|
|
||||||
checkOpen := func(rc io.ReadCloser, expected string) {
|
checkOpen := func(rc io.ReadCloser, expected string) {
|
||||||
|
t.Helper()
|
||||||
actual, err := io.ReadAll(rc)
|
actual, err := io.ReadAll(rc)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = rc.Close()
|
err = rc.Close()
|
||||||
|
@ -118,6 +119,7 @@ func TestMemoryObject(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
checkContent := func(o fs.Object, expected string) {
|
checkContent := func(o fs.Object, expected string) {
|
||||||
|
t.Helper()
|
||||||
rc, err := o.Open(context.Background())
|
rc, err := o.Open(context.Background())
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
checkOpen(rc, expected)
|
checkOpen(rc, expected)
|
||||||
|
@ -127,12 +129,28 @@ func TestMemoryObject(t *testing.T) {
|
||||||
|
|
||||||
rc, err := o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 3})
|
rc, err := o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 3})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
checkOpen(rc, "ot")
|
checkOpen(rc, "ota")
|
||||||
|
|
||||||
|
rc, err = o.Open(context.Background(), &fs.RangeOption{Start: 1, End: -1})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
checkOpen(rc, "otato")
|
||||||
|
|
||||||
|
rc, err = o.Open(context.Background(), &fs.RangeOption{Start: 1, End: 4096})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
checkOpen(rc, "otato")
|
||||||
|
|
||||||
|
rc, err = o.Open(context.Background(), &fs.RangeOption{Start: -1, End: 4})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
checkOpen(rc, "tato")
|
||||||
|
|
||||||
rc, err = o.Open(context.Background(), &fs.SeekOption{Offset: 3})
|
rc, err = o.Open(context.Background(), &fs.SeekOption{Offset: 3})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
checkOpen(rc, "ato")
|
checkOpen(rc, "ato")
|
||||||
|
|
||||||
|
rc, err = o.Open(context.Background(), &fs.SeekOption{Offset: -100})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
checkOpen(rc, "potato")
|
||||||
|
|
||||||
// check it fits within the buffer
|
// check it fits within the buffer
|
||||||
newNow = now.Add(2 * time.Minute)
|
newNow = now.Add(2 * time.Minute)
|
||||||
newContent := bytes.NewBufferString("Rutabaga")
|
newContent := bytes.NewBufferString("Rutabaga")
|
||||||
|
|
Loading…
Reference in New Issue