When calling modify, S3 normally resets headers and ACLs to a default
we probably don't want.
Keep the header and ACL the same (unless that's what is being
modified) by reading the header and ACL before invoking the modify,
then write the ACL after the modify completes.
... | ... |
@@ -619,18 +619,39 @@ class S3(object): |
619 | 619 |
debug("Received response '%s'" % (response)) |
620 | 620 |
return response |
621 | 621 |
|
622 |
+ @staticmethod |
|
623 |
+ def _sanitize_headers(headers): |
|
624 |
+ to_remove = ('content-length', |
|
625 |
+ 'x-amz-id-2', |
|
626 |
+ 'accept-ranges', |
|
627 |
+ 'x-amz-meta-s3cmd-attrs', |
|
628 |
+ 'server', |
|
629 |
+ 'last-modified', |
|
630 |
+ 'etag', |
|
631 |
+ 'x-amz-request-id', |
|
632 |
+ 'content-type') |
|
633 |
+ for h in to_remove: |
|
634 |
+ if h in headers: del headers[h] |
|
635 |
+ return headers |
|
636 |
+ |
|
622 | 637 |
def object_copy(self, src_uri, dst_uri, extra_headers = None): |
623 | 638 |
if src_uri.type != "s3": |
624 | 639 |
raise ValueError("Expected URI type 's3', got '%s'" % src_uri.type) |
625 | 640 |
if dst_uri.type != "s3": |
626 | 641 |
raise ValueError("Expected URI type 's3', got '%s'" % dst_uri.type) |
627 |
- headers = SortedDict(ignore_case = True) |
|
642 |
+ |
|
643 |
+ response = self.object_info(src_uri) |
|
644 |
+ headers = response['headers'] |
|
645 |
+ headers = self._sanitize_headers(headers) |
|
646 |
+ |
|
628 | 647 |
headers['x-amz-copy-source'] = "/%s/%s" % (src_uri.bucket(), self.urlencode_string(src_uri.object())) |
629 | 648 |
headers['x-amz-metadata-directive'] = "COPY" |
630 | 649 |
if self.config.acl_public: |
631 | 650 |
headers["x-amz-acl"] = "public-read" |
632 | 651 |
if self.config.reduced_redundancy: |
633 | 652 |
headers["x-amz-storage-class"] = "REDUCED_REDUNDANCY" |
653 |
+ else: |
|
654 |
+ headers["x-amz-storage-class"] = "STANDARD" |
|
634 | 655 |
|
635 | 656 |
## Set server side encryption |
636 | 657 |
if self.config.server_side_encryption: |
... | ... |
@@ -642,19 +663,22 @@ class S3(object): |
642 | 642 |
request = self.create_request("OBJECT_PUT", uri = dst_uri, headers = headers) |
643 | 643 |
response = self.send_request(request) |
644 | 644 |
return response |
645 |
- |
|
646 |
- def object_replace(self, src_uri, dst_uri, extra_headers = None): |
|
645 |
+ |
|
646 |
+ def object_modify(self, src_uri, dst_uri, extra_headers = None): |
|
647 | 647 |
if src_uri.type != "s3": |
648 | 648 |
raise ValueError("Expected URI type 's3', got '%s'" % src_uri.type) |
649 | 649 |
if dst_uri.type != "s3": |
650 | 650 |
raise ValueError("Expected URI type 's3', got '%s'" % dst_uri.type) |
651 |
- headers = SortedDict(ignore_case = True) |
|
651 |
+ |
|
652 |
+ info_response = self.object_info(src_uri) |
|
653 |
+ headers = info_response['headers'] |
|
654 |
+ headers = self._sanitize_headers(headers) |
|
655 |
+ acl = self.get_acl(src_uri) |
|
656 |
+ |
|
652 | 657 |
headers['x-amz-copy-source'] = "/%s/%s" % (src_uri.bucket(), self.urlencode_string(src_uri.object())) |
653 | 658 |
headers['x-amz-metadata-directive'] = "REPLACE" |
654 |
- if self.config.acl_public: |
|
655 |
- headers["x-amz-acl"] = "public-read" |
|
656 |
- if self.config.reduced_redundancy: |
|
657 |
- headers["x-amz-storage-class"] = "REDUCED_REDUNDANCY" |
|
659 |
+ |
|
660 |
+ # cannot change between standard and reduced redundancy with a REPLACE. |
|
658 | 661 |
|
659 | 662 |
## Set server side encryption |
660 | 663 |
if self.config.server_side_encryption: |
... | ... |
@@ -665,8 +689,11 @@ class S3(object): |
665 | 665 |
|
666 | 666 |
headers["content-type"] = self.content_type() |
667 | 667 |
|
668 |
- request = self.create_request("OBJECT_PUT", uri = dst_uri, headers = headers) |
|
668 |
+ request = self.create_request("OBJECT_PUT", uri = src_uri, headers = headers) |
|
669 | 669 |
response = self.send_request(request) |
670 |
+ |
|
671 |
+ acl_response = self.set_acl(src_uri, acl) |
|
672 |
+ |
|
670 | 673 |
return response |
671 | 674 |
|
672 | 675 |
def object_move(self, src_uri, dst_uri, extra_headers = None): |
... | ... |
@@ -753,7 +753,7 @@ def cmd_cp(args): |
753 | 753 |
|
754 | 754 |
def cmd_modify(args): |
755 | 755 |
s3 = S3(Config()) |
756 |
- return subcmd_cp_mv(args, s3.object_replace, "modify", u"File %(src)s modified") |
|
756 |
+ return subcmd_cp_mv(args, s3.object_modify, "modify", u"File %(src)s modified") |
|
757 | 757 |
|
758 | 758 |
def cmd_mv(args): |
759 | 759 |
s3 = S3(Config()) |
... | ... |
@@ -2172,6 +2172,7 @@ def main(): |
2172 | 2172 |
optparser.add_option( "--files-from", dest="files_from", action="append", metavar="FILE", help="Read list of source-file names from FILE. Use - to read from stdin.") |
2173 | 2173 |
optparser.add_option( "--bucket-location", dest="bucket_location", help="Datacentre to create bucket in. As of now the datacenters are: US (default), EU, ap-northeast-1, ap-southeast-1, sa-east-1, us-west-1 and us-west-2") |
2174 | 2174 |
optparser.add_option( "--reduced-redundancy", "--rr", dest="reduced_redundancy", action="store_true", help="Store object with 'Reduced redundancy'. Lower per-GB price. [put, cp, mv]") |
2175 |
+ optparser.add_option( "--no-reduced-redundancy", "--no-rr", dest="reduced_redundancy", action="store_false", help="Store object without 'Reduced redundancy'. Higher per-GB price. [put, cp, mv]") |
|
2175 | 2176 |
|
2176 | 2177 |
optparser.add_option( "--access-logging-target-prefix", dest="log_target_prefix", help="Target prefix for access logs (S3 URI) (for [cfmodify] and [accesslog] commands)") |
2177 | 2178 |
optparser.add_option( "--no-access-logging", dest="log_target_prefix", action="store_false", help="Disable access logging (for [cfmodify] and [accesslog] commands)") |