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.

574 lines
17 KiB

7 years ago
7 years ago
3 years ago
7 years ago
3 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
3 years ago
4 years ago
3 years ago
3 years ago
7 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
7 years ago
7 years ago
3 years ago
4 years ago
4 years ago
7 years ago
7 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. package s3api
  2. import (
  3. "context"
  4. "encoding/xml"
  5. "errors"
  6. "fmt"
  7. "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
  8. "github.com/seaweedfs/seaweedfs/weed/s3api/s3account"
  9. "github.com/seaweedfs/seaweedfs/weed/s3api/s3acl"
  10. "github.com/seaweedfs/seaweedfs/weed/util"
  11. "math"
  12. "net/http"
  13. "time"
  14. "github.com/seaweedfs/seaweedfs/weed/filer"
  15. "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
  16. "github.com/seaweedfs/seaweedfs/weed/storage/needle"
  17. "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
  18. "github.com/aws/aws-sdk-go/aws"
  19. "github.com/aws/aws-sdk-go/service/s3"
  20. "github.com/seaweedfs/seaweedfs/weed/glog"
  21. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  22. )
  23. type ListAllMyBucketsResult struct {
  24. XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListAllMyBucketsResult"`
  25. Owner *s3.Owner
  26. Buckets []*s3.Bucket `xml:"Buckets>Bucket"`
  27. }
  28. func (s3a *S3ApiServer) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
  29. glog.V(3).Infof("ListBucketsHandler")
  30. var identity *Identity
  31. var s3Err s3err.ErrorCode
  32. if s3a.iam.isEnabled() {
  33. identity, s3Err = s3a.iam.authUser(r)
  34. if s3Err != s3err.ErrNone {
  35. s3err.WriteErrorResponse(w, r, s3Err)
  36. return
  37. }
  38. }
  39. var response ListAllMyBucketsResult
  40. entries, _, err := s3a.list(s3a.option.BucketsPath, "", "", false, math.MaxInt32)
  41. if err != nil {
  42. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  43. return
  44. }
  45. identityId := r.Header.Get(s3_constants.AmzIdentityId)
  46. var buckets []*s3.Bucket
  47. for _, entry := range entries {
  48. if entry.IsDirectory {
  49. if identity != nil && !identity.canDo(s3_constants.ACTION_LIST, entry.Name, "") {
  50. continue
  51. }
  52. buckets = append(buckets, &s3.Bucket{
  53. Name: aws.String(entry.Name),
  54. CreationDate: aws.Time(time.Unix(entry.Attributes.Crtime, 0).UTC()),
  55. })
  56. }
  57. }
  58. response = ListAllMyBucketsResult{
  59. Owner: &s3.Owner{
  60. ID: aws.String(identityId),
  61. DisplayName: aws.String(identityId),
  62. },
  63. Buckets: buckets,
  64. }
  65. writeSuccessResponseXML(w, r, response)
  66. }
  67. func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
  68. bucket, _ := s3_constants.GetBucketAndObject(r)
  69. glog.V(3).Infof("PutBucketHandler %s", bucket)
  70. // avoid duplicated buckets
  71. errCode := s3err.ErrNone
  72. if err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  73. if resp, err := client.CollectionList(context.Background(), &filer_pb.CollectionListRequest{
  74. IncludeEcVolumes: true,
  75. IncludeNormalVolumes: true,
  76. }); err != nil {
  77. glog.Errorf("list collection: %v", err)
  78. return fmt.Errorf("list collections: %v", err)
  79. } else {
  80. for _, c := range resp.Collections {
  81. if bucket == c.Name {
  82. errCode = s3err.ErrBucketAlreadyExists
  83. break
  84. }
  85. }
  86. }
  87. return nil
  88. }); err != nil {
  89. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  90. return
  91. }
  92. if exist, err := s3a.exists(s3a.option.BucketsPath, bucket, true); err == nil && exist {
  93. errCode = s3err.ErrBucketAlreadyExists
  94. }
  95. if errCode != s3err.ErrNone {
  96. s3err.WriteErrorResponse(w, r, errCode)
  97. return
  98. }
  99. if s3a.iam.isEnabled() {
  100. if _, errCode = s3a.iam.authRequest(r, s3_constants.ACTION_ADMIN); errCode != s3err.ErrNone {
  101. s3err.WriteErrorResponse(w, r, errCode)
  102. return
  103. }
  104. }
  105. objectOwnership := r.Header.Get("X-Amz-Object-Ownership")
  106. requestAccountId := s3acl.GetAccountId(r)
  107. grants, errCode := s3acl.ExtractBucketAcl(r, s3a.accountManager, objectOwnership, requestAccountId, requestAccountId, true)
  108. if errCode != s3err.ErrNone {
  109. s3err.WriteErrorResponse(w, r, errCode)
  110. return
  111. }
  112. fn := func(entry *filer_pb.Entry) {
  113. if identityId := r.Header.Get(s3_constants.AmzIdentityId); identityId != "" {
  114. if entry.Extended == nil {
  115. entry.Extended = make(map[string][]byte)
  116. }
  117. entry.Extended[s3_constants.AmzIdentityId] = []byte(identityId)
  118. }
  119. if objectOwnership != "" {
  120. if entry.Extended == nil {
  121. entry.Extended = make(map[string][]byte)
  122. }
  123. entry.Extended[s3_constants.ExtOwnershipKey] = []byte(objectOwnership)
  124. }
  125. s3acl.AssembleEntryWithAcp(entry, requestAccountId, grants)
  126. }
  127. // create the folder for bucket, but lazily create actual collection
  128. if err := s3a.mkdir(s3a.option.BucketsPath, bucket, fn); err != nil {
  129. glog.Errorf("PutBucketHandler mkdir: %v", err)
  130. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  131. return
  132. }
  133. // clear cache
  134. s3a.bucketRegistry.ClearCache(bucket)
  135. w.Header().Set("Location", "/"+bucket)
  136. writeSuccessResponseEmpty(w, r)
  137. }
  138. func (s3a *S3ApiServer) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
  139. bucket, _ := s3_constants.GetBucketAndObject(r)
  140. glog.V(3).Infof("DeleteBucketHandler %s", bucket)
  141. if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
  142. s3err.WriteErrorResponse(w, r, err)
  143. return
  144. }
  145. err := s3a.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
  146. if !s3a.option.AllowDeleteBucketNotEmpty {
  147. entries, _, err := s3a.list(s3a.option.BucketsPath+"/"+bucket, "", "", false, 2)
  148. if err != nil {
  149. return fmt.Errorf("failed to list bucket %s: %v", bucket, err)
  150. }
  151. for _, entry := range entries {
  152. if entry.Name != s3_constants.MultipartUploadsFolder {
  153. return errors.New(s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code)
  154. }
  155. }
  156. }
  157. // delete collection
  158. deleteCollectionRequest := &filer_pb.DeleteCollectionRequest{
  159. Collection: bucket,
  160. }
  161. glog.V(1).Infof("delete collection: %v", deleteCollectionRequest)
  162. if _, err := client.DeleteCollection(context.Background(), deleteCollectionRequest); err != nil {
  163. return fmt.Errorf("delete collection %s: %v", bucket, err)
  164. }
  165. return nil
  166. })
  167. if err != nil {
  168. s3ErrorCode := s3err.ErrInternalError
  169. if err.Error() == s3err.GetAPIError(s3err.ErrBucketNotEmpty).Code {
  170. s3ErrorCode = s3err.ErrBucketNotEmpty
  171. }
  172. s3err.WriteErrorResponse(w, r, s3ErrorCode)
  173. return
  174. }
  175. err = s3a.rm(s3a.option.BucketsPath, bucket, false, true)
  176. if err != nil {
  177. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  178. return
  179. }
  180. s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
  181. }
  182. func (s3a *S3ApiServer) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
  183. bucket, _ := s3_constants.GetBucketAndObject(r)
  184. glog.V(3).Infof("HeadBucketHandler %s", bucket)
  185. _, errorCode := s3a.checkAccessForReadBucket(r, bucket, s3_constants.PermissionRead)
  186. if errorCode != s3err.ErrNone {
  187. s3err.WriteErrorResponse(w, r, errorCode)
  188. return
  189. }
  190. if entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket); entry == nil || err == filer_pb.ErrNotFound {
  191. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  192. return
  193. }
  194. writeSuccessResponseEmpty(w, r)
  195. }
  196. func (s3a *S3ApiServer) checkBucket(r *http.Request, bucket string) s3err.ErrorCode {
  197. entry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  198. if entry == nil || err == filer_pb.ErrNotFound {
  199. return s3err.ErrNoSuchBucket
  200. }
  201. if !s3a.hasAccess(r, entry) {
  202. return s3err.ErrAccessDenied
  203. }
  204. return s3err.ErrNone
  205. }
  206. func (s3a *S3ApiServer) hasAccess(r *http.Request, entry *filer_pb.Entry) bool {
  207. isAdmin := r.Header.Get(s3_constants.AmzIsAdmin) != ""
  208. if isAdmin {
  209. return true
  210. }
  211. if entry.Extended == nil {
  212. return true
  213. }
  214. identityId := r.Header.Get(s3_constants.AmzIdentityId)
  215. if id, ok := entry.Extended[s3_constants.AmzIdentityId]; ok {
  216. if identityId != string(id) {
  217. return false
  218. }
  219. }
  220. return true
  221. }
  222. // PutBucketAclHandler Put bucket ACL
  223. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketAcl.html
  224. func (s3a *S3ApiServer) PutBucketAclHandler(w http.ResponseWriter, r *http.Request) {
  225. bucket, _ := s3_constants.GetBucketAndObject(r)
  226. glog.V(3).Infof("PutBucketAclHandler %s", bucket)
  227. accountId := s3acl.GetAccountId(r)
  228. bucketMetadata, errorCode := s3a.checkAccessForPutBucketAcl(accountId, bucket)
  229. if errorCode != s3err.ErrNone {
  230. s3err.WriteErrorResponse(w, r, errorCode)
  231. return
  232. }
  233. grants, errCode := s3acl.ExtractBucketAcl(r, s3a.accountManager, bucketMetadata.ObjectOwnership, *bucketMetadata.Owner.ID, accountId, false)
  234. if errCode != s3err.ErrNone {
  235. s3err.WriteErrorResponse(w, r, errCode)
  236. return
  237. }
  238. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  239. if err != nil {
  240. glog.Warning(err)
  241. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  242. return
  243. }
  244. errCode = s3acl.AssembleEntryWithAcp(bucketEntry, *bucketMetadata.Owner.ID, grants)
  245. if errCode != s3err.ErrNone {
  246. s3err.WriteErrorResponse(w, r, errCode)
  247. return
  248. }
  249. err = updateBucketEntry(s3a, bucketEntry)
  250. if err != nil {
  251. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  252. return
  253. }
  254. //update local cache
  255. bucketMetadata.Acl = grants
  256. s3err.WriteEmptyResponse(w, r, http.StatusOK)
  257. }
  258. // GetBucketAclHandler Get Bucket ACL
  259. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketAcl.html
  260. func (s3a *S3ApiServer) GetBucketAclHandler(w http.ResponseWriter, r *http.Request) {
  261. // collect parameters
  262. bucket, _ := s3_constants.GetBucketAndObject(r)
  263. glog.V(3).Infof("GetBucketAclHandler %s", bucket)
  264. bucketMetadata, errorCode := s3a.checkAccessForReadBucket(r, bucket, s3_constants.PermissionReadAcp)
  265. if s3err.ErrNone != errorCode {
  266. s3err.WriteErrorResponse(w, r, errorCode)
  267. return
  268. }
  269. acp := &s3.PutBucketAclInput{
  270. AccessControlPolicy: &s3.AccessControlPolicy{
  271. Grants: bucketMetadata.Acl,
  272. Owner: bucketMetadata.Owner,
  273. },
  274. }
  275. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, acp)
  276. }
  277. // GetBucketLifecycleConfigurationHandler Get Bucket Lifecycle configuration
  278. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLifecycleConfiguration.html
  279. func (s3a *S3ApiServer) GetBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {
  280. // collect parameters
  281. bucket, _ := s3_constants.GetBucketAndObject(r)
  282. glog.V(3).Infof("GetBucketLifecycleConfigurationHandler %s", bucket)
  283. if err := s3a.checkBucket(r, bucket); err != s3err.ErrNone {
  284. s3err.WriteErrorResponse(w, r, err)
  285. return
  286. }
  287. fc, err := filer.ReadFilerConf(s3a.option.Filer, s3a.option.GrpcDialOption, nil)
  288. if err != nil {
  289. glog.Errorf("GetBucketLifecycleConfigurationHandler: %s", err)
  290. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  291. return
  292. }
  293. ttls := fc.GetCollectionTtls(bucket)
  294. if len(ttls) == 0 {
  295. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchLifecycleConfiguration)
  296. return
  297. }
  298. response := Lifecycle{}
  299. for prefix, internalTtl := range ttls {
  300. ttl, _ := needle.ReadTTL(internalTtl)
  301. days := int(ttl.Minutes() / 60 / 24)
  302. if days == 0 {
  303. continue
  304. }
  305. response.Rules = append(response.Rules, Rule{
  306. Status: Enabled, Filter: Filter{
  307. Prefix: Prefix{string: prefix, set: true},
  308. set: true,
  309. },
  310. Expiration: Expiration{Days: days, set: true},
  311. })
  312. }
  313. writeSuccessResponseXML(w, r, response)
  314. }
  315. // PutBucketLifecycleConfigurationHandler Put Bucket Lifecycle configuration
  316. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLifecycleConfiguration.html
  317. func (s3a *S3ApiServer) PutBucketLifecycleConfigurationHandler(w http.ResponseWriter, r *http.Request) {
  318. s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
  319. }
  320. // DeleteBucketMetricsConfiguration Delete Bucket Lifecycle
  321. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketLifecycle.html
  322. func (s3a *S3ApiServer) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) {
  323. s3err.WriteEmptyResponse(w, r, http.StatusNoContent)
  324. }
  325. // GetBucketLocationHandler Get bucket location
  326. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketLocation.html
  327. func (s3a *S3ApiServer) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
  328. writeSuccessResponseXML(w, r, LocationConstraint{})
  329. }
  330. // GetBucketRequestPaymentHandler Get bucket location
  331. // https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketRequestPayment.html
  332. func (s3a *S3ApiServer) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) {
  333. writeSuccessResponseXML(w, r, RequestPaymentConfiguration{Payer: "BucketOwner"})
  334. }
  335. // PutBucketOwnershipControls https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketOwnershipControls.html
  336. func (s3a *S3ApiServer) PutBucketOwnershipControls(w http.ResponseWriter, r *http.Request) {
  337. bucket, _ := s3_constants.GetBucketAndObject(r)
  338. glog.V(3).Infof("PutBucketOwnershipControls %s", bucket)
  339. errCode := s3a.checkAccessByOwnership(r, bucket)
  340. if errCode != s3err.ErrNone {
  341. s3err.WriteErrorResponse(w, r, errCode)
  342. return
  343. }
  344. if r.Body == nil || r.Body == http.NoBody {
  345. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  346. return
  347. }
  348. defer util.CloseRequest(r)
  349. var v s3.OwnershipControls
  350. err := xmlutil.UnmarshalXML(&v, xml.NewDecoder(r.Body), "")
  351. if err != nil {
  352. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  353. return
  354. }
  355. if len(v.Rules) != 1 {
  356. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  357. return
  358. }
  359. printOwnership := true
  360. newObjectOwnership := *v.Rules[0].ObjectOwnership
  361. switch newObjectOwnership {
  362. case s3_constants.OwnershipObjectWriter:
  363. case s3_constants.OwnershipBucketOwnerPreferred:
  364. case s3_constants.OwnershipBucketOwnerEnforced:
  365. default:
  366. s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRequest)
  367. return
  368. }
  369. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  370. if err != nil {
  371. if err == filer_pb.ErrNotFound {
  372. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  373. return
  374. }
  375. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  376. return
  377. }
  378. oldOwnership, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey]
  379. if !ok || string(oldOwnership) != newObjectOwnership {
  380. // must reset bucket acl to default(bucket owner with full control permission) before setting ownership
  381. // to `OwnershipBucketOwnerEnforced` (bucket cannot have ACLs set with ObjectOwnership's BucketOwnerEnforced setting)
  382. if newObjectOwnership == s3_constants.OwnershipBucketOwnerEnforced {
  383. acpGrants := s3acl.GetAcpGrants(nil, bucketEntry.Extended)
  384. if len(acpGrants) > 1 {
  385. s3err.WriteErrorResponse(w, r, s3err.InvalidBucketAclWithObjectOwnership)
  386. return
  387. } else if len(acpGrants) == 1 {
  388. bucketOwner := s3acl.GetAcpOwner(bucketEntry.Extended, s3account.AccountAdmin.Id)
  389. expectGrant := s3acl.GrantWithFullControl(bucketOwner)
  390. if !s3acl.GrantEquals(acpGrants[0], expectGrant) {
  391. s3err.WriteErrorResponse(w, r, s3err.InvalidBucketAclWithObjectOwnership)
  392. return
  393. }
  394. }
  395. }
  396. if bucketEntry.Extended == nil {
  397. bucketEntry.Extended = make(map[string][]byte)
  398. }
  399. //update local cache
  400. bucketMetadata, eCode := s3a.bucketRegistry.GetBucketMetadata(bucket)
  401. if eCode == s3err.ErrNone {
  402. bucketMetadata.ObjectOwnership = newObjectOwnership
  403. }
  404. bucketEntry.Extended[s3_constants.ExtOwnershipKey] = []byte(newObjectOwnership)
  405. err = s3a.updateEntry(s3a.option.BucketsPath, bucketEntry)
  406. if err != nil {
  407. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  408. return
  409. }
  410. }
  411. if printOwnership {
  412. result := &s3.PutBucketOwnershipControlsInput{
  413. OwnershipControls: &v,
  414. }
  415. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, result)
  416. } else {
  417. writeSuccessResponseEmpty(w, r)
  418. }
  419. }
  420. // GetBucketOwnershipControls https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetBucketOwnershipControls.html
  421. func (s3a *S3ApiServer) GetBucketOwnershipControls(w http.ResponseWriter, r *http.Request) {
  422. bucket, _ := s3_constants.GetBucketAndObject(r)
  423. glog.V(3).Infof("GetBucketOwnershipControls %s", bucket)
  424. errCode := s3a.checkAccessByOwnership(r, bucket)
  425. if errCode != s3err.ErrNone {
  426. s3err.WriteErrorResponse(w, r, errCode)
  427. return
  428. }
  429. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  430. if err != nil {
  431. if err == filer_pb.ErrNotFound {
  432. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  433. return
  434. }
  435. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  436. return
  437. }
  438. v, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey]
  439. if !ok {
  440. s3err.WriteErrorResponse(w, r, s3err.OwnershipControlsNotFoundError)
  441. return
  442. }
  443. ownership := string(v)
  444. result := &s3.PutBucketOwnershipControlsInput{
  445. OwnershipControls: &s3.OwnershipControls{
  446. Rules: []*s3.OwnershipControlsRule{
  447. {
  448. ObjectOwnership: &ownership,
  449. },
  450. },
  451. },
  452. }
  453. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, result)
  454. }
  455. // DeleteBucketOwnershipControls https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteBucketOwnershipControls.html
  456. func (s3a *S3ApiServer) DeleteBucketOwnershipControls(w http.ResponseWriter, r *http.Request) {
  457. bucket, _ := s3_constants.GetBucketAndObject(r)
  458. glog.V(3).Infof("PutBucketOwnershipControls %s", bucket)
  459. errCode := s3a.checkAccessByOwnership(r, bucket)
  460. if errCode != s3err.ErrNone {
  461. s3err.WriteErrorResponse(w, r, errCode)
  462. return
  463. }
  464. bucketEntry, err := s3a.getEntry(s3a.option.BucketsPath, bucket)
  465. if err != nil {
  466. if err == filer_pb.ErrNotFound {
  467. s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchBucket)
  468. return
  469. }
  470. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  471. return
  472. }
  473. _, ok := bucketEntry.Extended[s3_constants.ExtOwnershipKey]
  474. if !ok {
  475. s3err.WriteErrorResponse(w, r, s3err.OwnershipControlsNotFoundError)
  476. return
  477. }
  478. delete(bucketEntry.Extended, s3_constants.ExtOwnershipKey)
  479. err = s3a.updateEntry(s3a.option.BucketsPath, bucketEntry)
  480. if err != nil {
  481. s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
  482. return
  483. }
  484. emptyOwnershipControls := &s3.OwnershipControls{
  485. Rules: []*s3.OwnershipControlsRule{},
  486. }
  487. s3err.WriteAwsXMLResponse(w, r, http.StatusOK, emptyOwnershipControls)
  488. }