diff --git a/backend/common.go b/backend/common.go index aa11d14..b4ebf91 100644 --- a/backend/common.go +++ b/backend/common.go @@ -61,9 +61,9 @@ var ( // ParseRange parses input range header and returns startoffset, length, and // error. If no endoffset specified, then length is set to -1. -func ParseRange(file fs.FileInfo, acceptRange string) (int64, int64, error) { +func ParseRange(fi fs.FileInfo, acceptRange string) (int64, int64, error) { if acceptRange == "" { - return 0, file.Size(), nil + return 0, fi.Size(), nil } rangeKv := strings.Split(acceptRange, "=") diff --git a/backend/posix/posix.go b/backend/posix/posix.go index ae2a715..91f7782 100644 --- a/backend/posix/posix.go +++ b/backend/posix/posix.go @@ -1127,10 +1127,6 @@ func (p *Posix) DeleteObjects(ctx context.Context, input *s3.DeleteObjectsInput) func (p *Posix) GetObject(_ context.Context, input *s3.GetObjectInput, writer io.Writer) (*s3.GetObjectOutput, error) { bucket := *input.Bucket - object := *input.Key - acceptRange := *input.Range - var contentRange string - _, err := os.Stat(bucket) if errors.Is(err, fs.ErrNotExist) { return nil, s3err.GetAPIError(s3err.ErrNoSuchBucket) @@ -1139,6 +1135,7 @@ func (p *Posix) GetObject(_ context.Context, input *s3.GetObjectInput, writer io return nil, fmt.Errorf("stat bucket: %w", err) } + object := *input.Key objPath := filepath.Join(bucket, object) fi, err := os.Stat(objPath) if errors.Is(err, fs.ErrNotExist) { @@ -1148,21 +1145,59 @@ func (p *Posix) GetObject(_ context.Context, input *s3.GetObjectInput, writer io return nil, fmt.Errorf("stat object: %w", err) } + acceptRange := *input.Range startOffset, length, err := backend.ParseRange(fi, acceptRange) if err != nil { return nil, err } - if length == -1 { - length = fi.Size() - startOffset + 1 + objSize := fi.Size() + if fi.IsDir() { + // directory objects are always 0 len + objSize = 0 + length = 0 } - if startOffset+length > fi.Size()+1 { + if length == -1 { + length = objSize - startOffset + 1 + } + + if startOffset+length > objSize+1 { return nil, s3err.GetAPIError(s3err.ErrInvalidRange) } + var contentRange string if acceptRange != "" { - contentRange = fmt.Sprintf("bytes %v-%v/%v", startOffset, startOffset+length-1, fi.Size()) + contentRange = fmt.Sprintf("bytes %v-%v/%v", startOffset, startOffset+length-1, objSize) + } + + if fi.IsDir() { + userMetaData := make(map[string]string) + + contentType, contentEncoding := loadUserMetaData(objPath, userMetaData) + + b, err := xattr.Get(objPath, etagkey) + etag := string(b) + if err != nil { + etag = "" + } + + tags, err := p.getXattrTags(bucket, object) + if err != nil { + return nil, fmt.Errorf("get object tags: %w", err) + } + + return &s3.GetObjectOutput{ + AcceptRanges: &acceptRange, + ContentLength: length, + ContentEncoding: &contentEncoding, + ContentType: &contentType, + ETag: &etag, + LastModified: backend.GetTimePtr(fi.ModTime()), + Metadata: userMetaData, + TagCount: int32(len(tags)), + ContentRange: &contentRange, + }, nil } f, err := os.Open(objPath)