internal/gcsx/prefix_bucket.go (206 lines of code) (raw):
// Copyright 2015 Google LLC
//
// 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 gcsx
import (
"errors"
"strings"
"unicode/utf8"
"github.com/googlecloudplatform/gcsfuse/v2/internal/storage/gcs"
"golang.org/x/net/context"
)
// NewPrefixBucket creates a view on the wrapped bucket that pretends as if only
// the objects whose names contain the supplied string as a strict prefix exist,
// and that strips the prefix from the names of those objects before exposing them.
//
// In order to preserve the invariant that object names are valid UTF-8, prefix
// must be valid UTF-8.
func NewPrefixBucket(
prefix string,
wrapped gcs.Bucket) (b gcs.Bucket, err error) {
if !utf8.ValidString(prefix) {
err = errors.New("prefix is not valid UTF-8")
return
}
b = &prefixBucket{
prefix: prefix,
wrapped: wrapped,
}
return
}
type prefixBucket struct {
prefix string
wrapped gcs.Bucket
}
func (b *prefixBucket) wrappedName(n string) string {
return b.prefix + n
}
func (b *prefixBucket) localName(n string) string {
return strings.TrimPrefix(n, b.prefix)
}
func (b *prefixBucket) Name() string {
return b.wrapped.Name()
}
func (b *prefixBucket) BucketType() gcs.BucketType {
return b.wrapped.BucketType()
}
func (b *prefixBucket) NewReaderWithReadHandle(
ctx context.Context,
req *gcs.ReadObjectRequest) (rd gcs.StorageReader, err error) {
// Modify the request and call through.
mReq := new(gcs.ReadObjectRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
rd, err = b.wrapped.NewReaderWithReadHandle(ctx, mReq)
return
}
func (b *prefixBucket) CreateObject(
ctx context.Context,
req *gcs.CreateObjectRequest) (o *gcs.Object, err error) {
// Modify the request and call through.
mReq := new(gcs.CreateObjectRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
o, err = b.wrapped.CreateObject(ctx, mReq)
// Modify the returned object.
if o != nil {
o.Name = b.localName(o.Name)
}
return
}
func (b *prefixBucket) CreateObjectChunkWriter(ctx context.Context, req *gcs.CreateObjectRequest, chunkSize int, callBack func(bytesUploadedSoFar int64)) (gcs.Writer, error) {
// Modify the request and call through.
mReq := new(gcs.CreateObjectRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
wc, err := b.wrapped.CreateObjectChunkWriter(ctx, mReq, chunkSize, callBack)
if err != nil {
return nil, err
}
return wc, err
}
func (b *prefixBucket) FinalizeUpload(ctx context.Context, w gcs.Writer) (o *gcs.MinObject, err error) {
o, err = b.wrapped.FinalizeUpload(ctx, w)
// Modify the returned object.
if o != nil {
o.Name = b.localName(o.Name)
}
return
}
func (b *prefixBucket) FlushPendingWrites(ctx context.Context, w gcs.Writer) (o *gcs.MinObject, err error) {
return b.wrapped.FlushPendingWrites(ctx, w)
}
func (b *prefixBucket) CopyObject(
ctx context.Context,
req *gcs.CopyObjectRequest) (o *gcs.Object, err error) {
// Modify the request and call through.
mReq := new(gcs.CopyObjectRequest)
*mReq = *req
mReq.SrcName = b.wrappedName(req.SrcName)
mReq.DstName = b.wrappedName(req.DstName)
o, err = b.wrapped.CopyObject(ctx, mReq)
// Modify the returned object.
if o != nil {
o.Name = b.localName(o.Name)
}
return
}
func (b *prefixBucket) ComposeObjects(
ctx context.Context,
req *gcs.ComposeObjectsRequest) (o *gcs.Object, err error) {
// Modify the request and call through.
mReq := new(gcs.ComposeObjectsRequest)
*mReq = *req
mReq.DstName = b.wrappedName(req.DstName)
mReq.Sources = nil
for _, s := range req.Sources {
s.Name = b.wrappedName(s.Name)
mReq.Sources = append(mReq.Sources, s)
}
o, err = b.wrapped.ComposeObjects(ctx, mReq)
// Modify the returned object.
if o != nil {
o.Name = b.localName(o.Name)
}
return
}
func (b *prefixBucket) StatObject(
ctx context.Context,
req *gcs.StatObjectRequest) (m *gcs.MinObject, e *gcs.ExtendedObjectAttributes, err error) {
// Modify the request and call through.
mReq := new(gcs.StatObjectRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
m, e, err = b.wrapped.StatObject(ctx, mReq)
// Modify the returned object.
if m != nil {
m.Name = b.localName(m.Name)
}
return
}
func (b *prefixBucket) ListObjects(
ctx context.Context,
req *gcs.ListObjectsRequest) (l *gcs.Listing, err error) {
// Modify the request and call through.
mReq := new(gcs.ListObjectsRequest)
*mReq = *req
mReq.Prefix = b.prefix + mReq.Prefix
l, err = b.wrapped.ListObjects(ctx, mReq)
// Modify the returned listing.
if l != nil {
for _, o := range l.MinObjects {
o.Name = b.localName(o.Name)
}
for i, n := range l.CollapsedRuns {
l.CollapsedRuns[i] = strings.TrimPrefix(n, b.prefix)
}
}
return
}
func (b *prefixBucket) UpdateObject(
ctx context.Context,
req *gcs.UpdateObjectRequest) (o *gcs.Object, err error) {
// Modify the request and call through.
mReq := new(gcs.UpdateObjectRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
o, err = b.wrapped.UpdateObject(ctx, mReq)
// Modify the returned object.
if o != nil {
o.Name = b.localName(o.Name)
}
return
}
func (b *prefixBucket) DeleteObject(
ctx context.Context,
req *gcs.DeleteObjectRequest) (err error) {
// Modify the request and call through.
mReq := new(gcs.DeleteObjectRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
err = b.wrapped.DeleteObject(ctx, mReq)
return
}
func (b *prefixBucket) MoveObject(ctx context.Context, req *gcs.MoveObjectRequest) (*gcs.Object, error) {
// Modify the request and call through.
mReq := new(gcs.MoveObjectRequest)
*mReq = *req
mReq.SrcName = b.wrappedName(req.SrcName)
mReq.DstName = b.wrappedName(req.DstName)
o, err := b.wrapped.MoveObject(ctx, mReq)
// Modify the returned object.
if o != nil {
o.Name = b.localName(o.Name)
}
return o, err
}
func (b *prefixBucket) DeleteFolder(ctx context.Context, folderName string) (err error) {
mFolderName := b.wrappedName(folderName)
return b.wrapped.DeleteFolder(ctx, mFolderName)
}
func (b *prefixBucket) GetFolder(ctx context.Context, folderName string) (folder *gcs.Folder, err error) {
mFolderName := b.wrappedName(folderName)
f, err := b.wrapped.GetFolder(ctx, mFolderName)
// Modify the returned folder.
if f != nil {
f.Name = b.localName(f.Name)
}
return f, err
}
func (b *prefixBucket) CreateFolder(ctx context.Context, folderName string) (*gcs.Folder, error) {
mFolderName := b.wrappedName(folderName)
f, err := b.wrapped.CreateFolder(ctx, mFolderName)
// Modify the returned folder.
if f != nil {
f.Name = b.localName(mFolderName)
}
return f, err
}
func (b *prefixBucket) RenameFolder(ctx context.Context, folderName string, destinationFolderId string) (*gcs.Folder, error) {
mFolderName := b.wrappedName(folderName)
mDestinationFolderId := b.wrappedName(destinationFolderId)
f, err := b.wrapped.RenameFolder(ctx, mFolderName, mDestinationFolderId)
// Modify the returned folder.
if f != nil {
f.Name = b.localName(f.Name)
}
return f, err
}
func (b *prefixBucket) NewMultiRangeDownloader(
ctx context.Context, req *gcs.MultiRangeDownloaderRequest) (mrd gcs.MultiRangeDownloader, err error) {
// Modify the request and call through.
mReq := new(gcs.MultiRangeDownloaderRequest)
*mReq = *req
mReq.Name = b.wrappedName(req.Name)
mrd, err = b.wrapped.NewMultiRangeDownloader(ctx, mReq)
return
}