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