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.

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