mirror of
https://github.com/rclone/rclone.git
synced 2025-08-13 23:38:51 +02:00
serve s3: implement --auth-proxy
This implements --auth-proxy for serve s3. In addition it: * add listbuckets tests with and without authProxy * use auth proxy test framework * servetest: implement workaround for #7454 * update github.com/rclone/gofakes3 to fix race condition
This commit is contained in:
@ -25,22 +25,26 @@ var (
|
||||
// backend for gofakes3
|
||||
type s3Backend struct {
|
||||
opt *Options
|
||||
vfs *vfs.VFS
|
||||
s *Server
|
||||
meta *sync.Map
|
||||
}
|
||||
|
||||
// newBackend creates a new SimpleBucketBackend.
|
||||
func newBackend(vfs *vfs.VFS, opt *Options) gofakes3.Backend {
|
||||
func newBackend(s *Server, opt *Options) gofakes3.Backend {
|
||||
return &s3Backend{
|
||||
vfs: vfs,
|
||||
opt: opt,
|
||||
s: s,
|
||||
meta: new(sync.Map),
|
||||
}
|
||||
}
|
||||
|
||||
// ListBuckets always returns the default bucket.
|
||||
func (b *s3Backend) ListBuckets(ctx context.Context) ([]gofakes3.BucketInfo, error) {
|
||||
dirEntries, err := getDirEntries("/", b.vfs)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dirEntries, err := getDirEntries("/", _vfs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -60,7 +64,11 @@ func (b *s3Backend) ListBuckets(ctx context.Context) ([]gofakes3.BucketInfo, err
|
||||
|
||||
// ListBucket lists the objects in the given bucket.
|
||||
func (b *s3Backend) ListBucket(ctx context.Context, bucket string, prefix *gofakes3.Prefix, page gofakes3.ListBucketPage) (*gofakes3.ObjectList, error) {
|
||||
_, err := b.vfs.Stat(bucket)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = _vfs.Stat(bucket)
|
||||
if err != nil {
|
||||
return nil, gofakes3.BucketNotFound(bucket)
|
||||
}
|
||||
@ -79,7 +87,7 @@ func (b *s3Backend) ListBucket(ctx context.Context, bucket string, prefix *gofak
|
||||
response := gofakes3.NewObjectList()
|
||||
path, remaining := prefixParser(prefix)
|
||||
|
||||
err = b.entryListR(bucket, path, remaining, prefix.HasDelimiter, response)
|
||||
err = b.entryListR(_vfs, bucket, path, remaining, prefix.HasDelimiter, response)
|
||||
if err == gofakes3.ErrNoSuchKey {
|
||||
// AWS just returns an empty list
|
||||
response = gofakes3.NewObjectList()
|
||||
@ -94,13 +102,17 @@ func (b *s3Backend) ListBucket(ctx context.Context, bucket string, prefix *gofak
|
||||
//
|
||||
// Note that the metadata is not supported yet.
|
||||
func (b *s3Backend) HeadObject(ctx context.Context, bucketName, objectName string) (*gofakes3.Object, error) {
|
||||
_, err := b.vfs.Stat(bucketName)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = _vfs.Stat(bucketName)
|
||||
if err != nil {
|
||||
return nil, gofakes3.BucketNotFound(bucketName)
|
||||
}
|
||||
|
||||
fp := path.Join(bucketName, objectName)
|
||||
node, err := b.vfs.Stat(fp)
|
||||
node, err := _vfs.Stat(fp)
|
||||
if err != nil {
|
||||
return nil, gofakes3.KeyNotFound(objectName)
|
||||
}
|
||||
@ -141,13 +153,17 @@ func (b *s3Backend) HeadObject(ctx context.Context, bucketName, objectName strin
|
||||
|
||||
// GetObject fetchs the object from the filesystem.
|
||||
func (b *s3Backend) GetObject(ctx context.Context, bucketName, objectName string, rangeRequest *gofakes3.ObjectRangeRequest) (obj *gofakes3.Object, err error) {
|
||||
_, err = b.vfs.Stat(bucketName)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = _vfs.Stat(bucketName)
|
||||
if err != nil {
|
||||
return nil, gofakes3.BucketNotFound(bucketName)
|
||||
}
|
||||
|
||||
fp := path.Join(bucketName, objectName)
|
||||
node, err := b.vfs.Stat(fp)
|
||||
node, err := _vfs.Stat(fp)
|
||||
if err != nil {
|
||||
return nil, gofakes3.KeyNotFound(objectName)
|
||||
}
|
||||
@ -223,9 +239,13 @@ func (b *s3Backend) storeModtime(fp string, meta map[string]string, val string)
|
||||
|
||||
// TouchObject creates or updates meta on specified object.
|
||||
func (b *s3Backend) TouchObject(ctx context.Context, fp string, meta map[string]string) (result gofakes3.PutObjectResult, err error) {
|
||||
_, err = b.vfs.Stat(fp)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
_, err = _vfs.Stat(fp)
|
||||
if err == vfs.ENOENT {
|
||||
f, err := b.vfs.Create(fp)
|
||||
f, err := _vfs.Create(fp)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@ -235,7 +255,7 @@ func (b *s3Backend) TouchObject(ctx context.Context, fp string, meta map[string]
|
||||
return result, err
|
||||
}
|
||||
|
||||
_, err = b.vfs.Stat(fp)
|
||||
_, err = _vfs.Stat(fp)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@ -246,7 +266,7 @@ func (b *s3Backend) TouchObject(ctx context.Context, fp string, meta map[string]
|
||||
ti, err := swift.FloatStringToTime(val)
|
||||
if err == nil {
|
||||
b.storeModtime(fp, meta, val)
|
||||
return result, b.vfs.Chtimes(fp, ti, ti)
|
||||
return result, _vfs.Chtimes(fp, ti, ti)
|
||||
}
|
||||
// ignore error since the file is successfully created
|
||||
}
|
||||
@ -255,7 +275,7 @@ func (b *s3Backend) TouchObject(ctx context.Context, fp string, meta map[string]
|
||||
ti, err := swift.FloatStringToTime(val)
|
||||
if err == nil {
|
||||
b.storeModtime(fp, meta, val)
|
||||
return result, b.vfs.Chtimes(fp, ti, ti)
|
||||
return result, _vfs.Chtimes(fp, ti, ti)
|
||||
}
|
||||
// ignore error since the file is successfully created
|
||||
}
|
||||
@ -270,7 +290,11 @@ func (b *s3Backend) PutObject(
|
||||
meta map[string]string,
|
||||
input io.Reader, size int64,
|
||||
) (result gofakes3.PutObjectResult, err error) {
|
||||
_, err = b.vfs.Stat(bucketName)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
_, err = _vfs.Stat(bucketName)
|
||||
if err != nil {
|
||||
return result, gofakes3.BucketNotFound(bucketName)
|
||||
}
|
||||
@ -284,12 +308,12 @@ func (b *s3Backend) PutObject(
|
||||
// }
|
||||
|
||||
if objectDir != "." {
|
||||
if err := mkdirRecursive(objectDir, b.vfs); err != nil {
|
||||
if err := mkdirRecursive(objectDir, _vfs); err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
|
||||
f, err := b.vfs.Create(fp)
|
||||
f, err := _vfs.Create(fp)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@ -297,17 +321,17 @@ func (b *s3Backend) PutObject(
|
||||
if _, err := io.Copy(f, input); err != nil {
|
||||
// remove file when i/o error occurred (FsPutErr)
|
||||
_ = f.Close()
|
||||
_ = b.vfs.Remove(fp)
|
||||
_ = _vfs.Remove(fp)
|
||||
return result, err
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
// remove file when close error occurred (FsPutErr)
|
||||
_ = b.vfs.Remove(fp)
|
||||
_ = _vfs.Remove(fp)
|
||||
return result, err
|
||||
}
|
||||
|
||||
_, err = b.vfs.Stat(fp)
|
||||
_, err = _vfs.Stat(fp)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
@ -318,16 +342,13 @@ func (b *s3Backend) PutObject(
|
||||
ti, err := swift.FloatStringToTime(val)
|
||||
if err == nil {
|
||||
b.storeModtime(fp, meta, val)
|
||||
return result, b.vfs.Chtimes(fp, ti, ti)
|
||||
return result, _vfs.Chtimes(fp, ti, ti)
|
||||
}
|
||||
// ignore error since the file is successfully created
|
||||
}
|
||||
|
||||
if val, ok := meta["mtime"]; ok {
|
||||
ti, err := swift.FloatStringToTime(val)
|
||||
if err == nil {
|
||||
if val, ok := meta["mtime"]; ok {
|
||||
b.storeModtime(fp, meta, val)
|
||||
return result, b.vfs.Chtimes(fp, ti, ti)
|
||||
return result, _vfs.Chtimes(fp, ti, ti)
|
||||
}
|
||||
// ignore error since the file is successfully created
|
||||
}
|
||||
@ -338,7 +359,7 @@ func (b *s3Backend) PutObject(
|
||||
// DeleteMulti deletes multiple objects in a single request.
|
||||
func (b *s3Backend) DeleteMulti(ctx context.Context, bucketName string, objects ...string) (result gofakes3.MultiDeleteResult, rerr error) {
|
||||
for _, object := range objects {
|
||||
if err := b.deleteObject(bucketName, object); err != nil {
|
||||
if err := b.deleteObject(ctx, bucketName, object); err != nil {
|
||||
fs.Errorf("serve s3", "delete object failed: %v", err)
|
||||
result.Error = append(result.Error, gofakes3.ErrorResult{
|
||||
Code: gofakes3.ErrInternal,
|
||||
@ -357,12 +378,16 @@ func (b *s3Backend) DeleteMulti(ctx context.Context, bucketName string, objects
|
||||
|
||||
// DeleteObject deletes the object with the given name.
|
||||
func (b *s3Backend) DeleteObject(ctx context.Context, bucketName, objectName string) (result gofakes3.ObjectDeleteResult, rerr error) {
|
||||
return result, b.deleteObject(bucketName, objectName)
|
||||
return result, b.deleteObject(ctx, bucketName, objectName)
|
||||
}
|
||||
|
||||
// deleteObject deletes the object from the filesystem.
|
||||
func (b *s3Backend) deleteObject(bucketName, objectName string) error {
|
||||
_, err := b.vfs.Stat(bucketName)
|
||||
func (b *s3Backend) deleteObject(ctx context.Context, bucketName, objectName string) error {
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = _vfs.Stat(bucketName)
|
||||
if err != nil {
|
||||
return gofakes3.BucketNotFound(bucketName)
|
||||
}
|
||||
@ -370,18 +395,22 @@ func (b *s3Backend) deleteObject(bucketName, objectName string) error {
|
||||
fp := path.Join(bucketName, objectName)
|
||||
// S3 does not report an error when attemping to delete a key that does not exist, so
|
||||
// we need to skip IsNotExist errors.
|
||||
if err := b.vfs.Remove(fp); err != nil && !os.IsNotExist(err) {
|
||||
if err := _vfs.Remove(fp); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
// FIXME: unsafe operation
|
||||
rmdirRecursive(fp, b.vfs)
|
||||
rmdirRecursive(fp, _vfs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateBucket creates a new bucket.
|
||||
func (b *s3Backend) CreateBucket(ctx context.Context, name string) error {
|
||||
_, err := b.vfs.Stat(name)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = _vfs.Stat(name)
|
||||
if err != nil && err != vfs.ENOENT {
|
||||
return gofakes3.ErrInternal
|
||||
}
|
||||
@ -390,7 +419,7 @@ func (b *s3Backend) CreateBucket(ctx context.Context, name string) error {
|
||||
return gofakes3.ErrBucketAlreadyExists
|
||||
}
|
||||
|
||||
if err := b.vfs.Mkdir(name, 0755); err != nil {
|
||||
if err := _vfs.Mkdir(name, 0755); err != nil {
|
||||
return gofakes3.ErrInternal
|
||||
}
|
||||
return nil
|
||||
@ -398,12 +427,16 @@ func (b *s3Backend) CreateBucket(ctx context.Context, name string) error {
|
||||
|
||||
// DeleteBucket deletes the bucket with the given name.
|
||||
func (b *s3Backend) DeleteBucket(ctx context.Context, name string) error {
|
||||
_, err := b.vfs.Stat(name)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = _vfs.Stat(name)
|
||||
if err != nil {
|
||||
return gofakes3.BucketNotFound(name)
|
||||
}
|
||||
|
||||
if err := b.vfs.Remove(name); err != nil {
|
||||
if err := _vfs.Remove(name); err != nil {
|
||||
return gofakes3.ErrBucketNotEmpty
|
||||
}
|
||||
|
||||
@ -412,7 +445,11 @@ func (b *s3Backend) DeleteBucket(ctx context.Context, name string) error {
|
||||
|
||||
// BucketExists checks if the bucket exists.
|
||||
func (b *s3Backend) BucketExists(ctx context.Context, name string) (exists bool, err error) {
|
||||
_, err = b.vfs.Stat(name)
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
_, err = _vfs.Stat(name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
@ -422,6 +459,10 @@ func (b *s3Backend) BucketExists(ctx context.Context, name string) (exists bool,
|
||||
|
||||
// CopyObject copy specified object from srcKey to dstKey.
|
||||
func (b *s3Backend) CopyObject(ctx context.Context, srcBucket, srcKey, dstBucket, dstKey string, meta map[string]string) (result gofakes3.CopyObjectResult, err error) {
|
||||
_vfs, err := b.s.getVFS(ctx)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
fp := path.Join(srcBucket, srcKey)
|
||||
if srcBucket == dstBucket && srcKey == dstKey {
|
||||
b.meta.Store(fp, meta)
|
||||
@ -439,10 +480,10 @@ func (b *s3Backend) CopyObject(ctx context.Context, srcBucket, srcKey, dstBucket
|
||||
}
|
||||
b.storeModtime(fp, meta, val)
|
||||
|
||||
return result, b.vfs.Chtimes(fp, ti, ti)
|
||||
return result, _vfs.Chtimes(fp, ti, ti)
|
||||
}
|
||||
|
||||
cStat, err := b.vfs.Stat(fp)
|
||||
cStat, err := _vfs.Stat(fp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user