You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							217 lines
						
					
					
						
							6.2 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							217 lines
						
					
					
						
							6.2 KiB
						
					
					
				
								package s3api
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"context"
							 | 
						|
									"encoding/json"
							 | 
						|
									"github.com/aws/aws-sdk-go/service/s3"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util"
							 | 
						|
									"math"
							 | 
						|
									"sync"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								var loadBucketMetadataFromFiler = func(r *BucketRegistry, bucketName string) (*BucketMetaData, error) {
							 | 
						|
									entry, err := filer_pb.GetEntry(context.Background(), r.s3a, util.NewFullPath(r.s3a.option.BucketsPath, bucketName))
							 | 
						|
									if err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return buildBucketMetadata(r.s3a.iam, entry), nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type BucketMetaData struct {
							 | 
						|
									_ struct{} `type:"structure"`
							 | 
						|
								
							 | 
						|
									Name string
							 | 
						|
								
							 | 
						|
									//By default, when another AWS account uploads an object to S3 bucket,
							 | 
						|
									//that account (the object writer) owns the object, has access to it, and
							 | 
						|
									//can grant other users access to it through ACLs. You can use Object Ownership
							 | 
						|
									//to change this default behavior so that ACLs are disabled and you, as the
							 | 
						|
									//bucket owner, automatically own every object in your bucket.
							 | 
						|
									ObjectOwnership string
							 | 
						|
								
							 | 
						|
									// Container for the bucket owner's display name and ID.
							 | 
						|
									Owner *s3.Owner `type:"structure"`
							 | 
						|
								
							 | 
						|
									// A list of grants for access controls.
							 | 
						|
									Acl []*s3.Grant `locationName:"AccessControlList" locationNameList:"Grant" type:"list"`
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type BucketRegistry struct {
							 | 
						|
									metadataCache     map[string]*BucketMetaData
							 | 
						|
									metadataCacheLock sync.RWMutex
							 | 
						|
								
							 | 
						|
									notFound     map[string]struct{}
							 | 
						|
									notFoundLock sync.RWMutex
							 | 
						|
									s3a          *S3ApiServer
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func NewBucketRegistry(s3a *S3ApiServer) *BucketRegistry {
							 | 
						|
									br := &BucketRegistry{
							 | 
						|
										metadataCache: make(map[string]*BucketMetaData),
							 | 
						|
										notFound:      make(map[string]struct{}),
							 | 
						|
										s3a:           s3a,
							 | 
						|
									}
							 | 
						|
									err := br.init()
							 | 
						|
									if err != nil {
							 | 
						|
										glog.Fatal("init bucket registry failed", err)
							 | 
						|
										return nil
							 | 
						|
									}
							 | 
						|
									return br
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) init() error {
							 | 
						|
									err := filer_pb.List(context.Background(), r.s3a, r.s3a.option.BucketsPath, "", func(entry *filer_pb.Entry, isLast bool) error {
							 | 
						|
										r.LoadBucketMetadata(entry)
							 | 
						|
										return nil
							 | 
						|
									}, "", false, math.MaxUint32)
							 | 
						|
									return err
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) LoadBucketMetadata(entry *filer_pb.Entry) {
							 | 
						|
									bucketMetadata := buildBucketMetadata(r.s3a.iam, entry)
							 | 
						|
									r.metadataCacheLock.Lock()
							 | 
						|
									defer r.metadataCacheLock.Unlock()
							 | 
						|
									r.metadataCache[entry.Name] = bucketMetadata
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func buildBucketMetadata(accountManager AccountManager, entry *filer_pb.Entry) *BucketMetaData {
							 | 
						|
									entryJson, _ := json.Marshal(entry)
							 | 
						|
									glog.V(3).Infof("build bucket metadata,entry=%s", entryJson)
							 | 
						|
									bucketMetadata := &BucketMetaData{
							 | 
						|
										Name: entry.Name,
							 | 
						|
								
							 | 
						|
										//Default ownership: OwnershipBucketOwnerEnforced, which means Acl is disabled
							 | 
						|
										ObjectOwnership: s3_constants.OwnershipBucketOwnerEnforced,
							 | 
						|
								
							 | 
						|
										// Default owner: `AccountAdmin`
							 | 
						|
										Owner: &s3.Owner{
							 | 
						|
											ID:          &AccountAdmin.Id,
							 | 
						|
											DisplayName: &AccountAdmin.DisplayName,
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
									if entry.Extended != nil {
							 | 
						|
										//ownership control
							 | 
						|
										ownership, ok := entry.Extended[s3_constants.ExtOwnershipKey]
							 | 
						|
										if ok {
							 | 
						|
											ownership := string(ownership)
							 | 
						|
											valid := s3_constants.ValidateOwnership(ownership)
							 | 
						|
											if valid {
							 | 
						|
												bucketMetadata.ObjectOwnership = ownership
							 | 
						|
											} else {
							 | 
						|
												glog.Warningf("Invalid ownership: %s, bucket: %s", ownership, bucketMetadata.Name)
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										//access control policy
							 | 
						|
										//owner
							 | 
						|
										acpOwnerBytes, ok := entry.Extended[s3_constants.ExtAmzOwnerKey]
							 | 
						|
										if ok && len(acpOwnerBytes) > 0 {
							 | 
						|
											ownerAccountId := string(acpOwnerBytes)
							 | 
						|
											ownerAccountName := accountManager.GetAccountNameById(ownerAccountId)
							 | 
						|
											if ownerAccountName == "" {
							 | 
						|
												glog.Warningf("owner[id=%s] is invalid, bucket: %s", ownerAccountId, bucketMetadata.Name)
							 | 
						|
											} else {
							 | 
						|
												bucketMetadata.Owner = &s3.Owner{
							 | 
						|
													ID:          &ownerAccountId,
							 | 
						|
													DisplayName: &ownerAccountName,
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										//grants
							 | 
						|
										acpGrantsBytes, ok := entry.Extended[s3_constants.ExtAmzAclKey]
							 | 
						|
										if ok && len(acpGrantsBytes) > 0 {
							 | 
						|
											var grants []*s3.Grant
							 | 
						|
											err := json.Unmarshal(acpGrantsBytes, &grants)
							 | 
						|
											if err == nil {
							 | 
						|
												bucketMetadata.Acl = grants
							 | 
						|
											} else {
							 | 
						|
												glog.Warningf("Unmarshal ACP grants: %s(%v), bucket: %s", string(acpGrantsBytes), err, bucketMetadata.Name)
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return bucketMetadata
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) RemoveBucketMetadata(entry *filer_pb.Entry) {
							 | 
						|
									r.removeMetadataCache(entry.Name)
							 | 
						|
									r.unMarkNotFound(entry.Name)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) GetBucketMetadata(bucketName string) (*BucketMetaData, s3err.ErrorCode) {
							 | 
						|
									r.metadataCacheLock.RLock()
							 | 
						|
									bucketMetadata, ok := r.metadataCache[bucketName]
							 | 
						|
									r.metadataCacheLock.RUnlock()
							 | 
						|
									if ok {
							 | 
						|
										return bucketMetadata, s3err.ErrNone
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									r.notFoundLock.RLock()
							 | 
						|
									_, ok = r.notFound[bucketName]
							 | 
						|
									r.notFoundLock.RUnlock()
							 | 
						|
									if ok {
							 | 
						|
										return nil, s3err.ErrNoSuchBucket
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									bucketMetadata, errCode := r.LoadBucketMetadataFromFiler(bucketName)
							 | 
						|
									if errCode != s3err.ErrNone {
							 | 
						|
										return nil, errCode
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									r.setMetadataCache(bucketMetadata)
							 | 
						|
									r.unMarkNotFound(bucketName)
							 | 
						|
									return bucketMetadata, s3err.ErrNone
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) LoadBucketMetadataFromFiler(bucketName string) (*BucketMetaData, s3err.ErrorCode) {
							 | 
						|
									r.notFoundLock.Lock()
							 | 
						|
									defer r.notFoundLock.Unlock()
							 | 
						|
								
							 | 
						|
									//check if already exists
							 | 
						|
									r.metadataCacheLock.RLock()
							 | 
						|
									bucketMetaData, ok := r.metadataCache[bucketName]
							 | 
						|
									r.metadataCacheLock.RUnlock()
							 | 
						|
									if ok {
							 | 
						|
										return bucketMetaData, s3err.ErrNone
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									//if not exists, load from filer
							 | 
						|
									bucketMetadata, err := loadBucketMetadataFromFiler(r, bucketName)
							 | 
						|
									if err != nil {
							 | 
						|
										if err == filer_pb.ErrNotFound {
							 | 
						|
											// The bucket doesn't actually exist and should no longer loaded from the filer
							 | 
						|
											r.notFound[bucketName] = struct{}{}
							 | 
						|
											return nil, s3err.ErrNoSuchBucket
							 | 
						|
										}
							 | 
						|
										return nil, s3err.ErrInternalError
							 | 
						|
									}
							 | 
						|
									return bucketMetadata, s3err.ErrNone
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) setMetadataCache(metadata *BucketMetaData) {
							 | 
						|
									r.metadataCacheLock.Lock()
							 | 
						|
									defer r.metadataCacheLock.Unlock()
							 | 
						|
									r.metadataCache[metadata.Name] = metadata
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) removeMetadataCache(bucket string) {
							 | 
						|
									r.metadataCacheLock.Lock()
							 | 
						|
									defer r.metadataCacheLock.Unlock()
							 | 
						|
									delete(r.metadataCache, bucket)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) markNotFound(bucket string) {
							 | 
						|
									r.notFoundLock.Lock()
							 | 
						|
									defer r.notFoundLock.Unlock()
							 | 
						|
									r.notFound[bucket] = struct{}{}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (r *BucketRegistry) unMarkNotFound(bucket string) {
							 | 
						|
									r.notFoundLock.Lock()
							 | 
						|
									defer r.notFoundLock.Unlock()
							 | 
						|
									delete(r.notFound, bucket)
							 | 
						|
								}
							 |