mirror of
https://github.com/perkeep/perkeep.git
synced 2025-02-26 20:25:07 +00:00

testSubFetcher in blobserver/storagetest was already checking that we'd get specific error messages in the case of negative input parameters or an out of range offset. This change rationalizes these constraints with named errors (ErrNegativeSubFetch and ErrOutOfRangeOffsetSubFetch) specified in the SubFetcher interface. It also fixes the googlestorage and s3 implementations so that they pass the aforementioned test. Change-Id: I25b72b842855b90ee3cab44c90654581dccf4b8e
83 lines
2.1 KiB
Go
83 lines
2.1 KiB
Go
/*
|
|
Copyright 2014 The Camlistore AUTHORS
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package blobpacked
|
|
|
|
import (
|
|
"io"
|
|
"io/ioutil"
|
|
|
|
"camlistore.org/pkg/blob"
|
|
)
|
|
|
|
// Ensure we implement the optional interface correctly.
|
|
var _ blob.SubFetcher = (*storage)(nil)
|
|
|
|
// SubFetch returns part of a blob.
|
|
// The caller must close the returned io.ReadCloser.
|
|
// The Reader may return fewer than 'length' bytes. Callers should
|
|
// check. The returned error should be os.ErrNotExist if the blob
|
|
// doesn't exist.
|
|
func (s *storage) SubFetch(ref blob.Ref, offset, length int64) (io.ReadCloser, error) {
|
|
m, err := s.getMetaRow(ref)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if m.isPacked() {
|
|
length, err = capOffsetLength(m.size, offset, length)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// get the blob from the large subfetcher
|
|
return s.large.SubFetch(m.largeRef, int64(m.largeOff)+offset, length)
|
|
}
|
|
if sf, ok := s.small.(blob.SubFetcher); ok {
|
|
return sf.SubFetch(ref, offset, length)
|
|
}
|
|
rc, size, err := s.small.Fetch(ref)
|
|
if err != nil {
|
|
return rc, err
|
|
}
|
|
length, err = capOffsetLength(size, offset, length)
|
|
if err != nil {
|
|
rc.Close()
|
|
return nil, err
|
|
}
|
|
if offset != 0 {
|
|
if _, err = io.CopyN(ioutil.Discard, rc, offset); err != nil {
|
|
rc.Close()
|
|
return nil, err
|
|
}
|
|
}
|
|
return struct {
|
|
io.Reader
|
|
io.Closer
|
|
}{io.LimitReader(rc, length), rc}, nil
|
|
}
|
|
|
|
func capOffsetLength(size uint32, offset, length int64) (newLength int64, err error) {
|
|
if offset < 0 || length < 0 {
|
|
return 0, blob.ErrNegativeSubFetch
|
|
}
|
|
if offset > int64(size) {
|
|
return 0, blob.ErrOutOfRangeOffsetSubFetch
|
|
}
|
|
if over := (offset + length) - int64(size); over > 0 {
|
|
length -= over
|
|
}
|
|
return length, nil
|
|
}
|