Browse code

Merge branch 'xattr' into merge

Matt Domsch authored on 2013/05/15 13:27:04
Showing 5 changed files
... ...
@@ -30,6 +30,7 @@ class Config(object):
30 30
     send_chunk = 4096
31 31
     recv_chunk = 4096
32 32
     list_md5 = False
33
+    md5_xattr = None
33 34
     human_readable_sizes = False
34 35
     extra_headers = SortedDict(ignore_case = True)
35 36
     force = False
... ...
@@ -16,6 +16,12 @@ import base64
16 16
 import errno
17 17
 import urllib
18 18
 
19
+try:
20
+    import xattr
21
+    have_xattr = True
22
+except:
23
+    have_xattr = False
24
+
19 25
 from logging import debug, info, warning, error
20 26
 
21 27
 
... ...
@@ -227,6 +233,18 @@ def mktmpfile(prefix = "/tmp/tmpfile-", randchars = 20):
227 227
 __all__.append("mktmpfile")
228 228
 
229 229
 def hash_file_md5(filename):
230
+    md5_xattr = Config.Config().md5_xattr
231
+    if have_xattr and md5_xattr is not None:
232
+        try:
233
+            md5sum = xattr.get(filename, md5_xattr, namespace=xattr.NS_USER)
234
+            debug("xattr.get(%s, %s) returned %s" % (filename, md5_xattr, md5sum))
235
+            return md5sum
236
+        except:
237
+            pass
238
+    return hash_file_md5_io(filename)
239
+__all__.append("hash_file_md5")
240
+
241
+def hash_file_md5_io(filename):
230 242
     h = md5()
231 243
     f = open(filename, "rb")
232 244
     while True:
... ...
@@ -237,7 +255,6 @@ def hash_file_md5(filename):
237 237
         h.update(data)
238 238
     f.close()
239 239
     return h.hexdigest()
240
-__all__.append("hash_file_md5")
241 240
 
242 241
 def mkdir_with_parents(dir_name):
243 242
     """
... ...
@@ -12,6 +12,7 @@ import re
12 12
 from subprocess import Popen, PIPE, STDOUT
13 13
 import locale
14 14
 import pwd
15
+import xattr
15 16
 
16 17
 count_pass = 0
17 18
 count_fail = 0
... ...
@@ -25,8 +26,10 @@ verbose = False
25 25
 
26 26
 if os.name == "posix":
27 27
     have_wget = True
28
+    have_md5sum = True
28 29
 elif os.name == "nt":
29 30
     have_wget = False
31
+    have_md5sum = False
30 32
 else:
31 33
     print "Unknown platform: %s" % os.name
32 34
     sys.exit(1)
... ...
@@ -404,6 +407,15 @@ test_s3cmd("Don't check MD5", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1),
404 404
 test_s3cmd("Check MD5", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--check-md5'],
405 405
     must_find = [ "cksum1.txt" ])
406 406
 
407
+## ====== Check MD5 sum on Sync with Extended Attributes
408
+if 'xattr' in sys.modules.keys() and have_md5sum:
409
+    os.system("echo 1234566789012 > testsuite/checksum/cksum4.txt")
410
+    test_s3cmd("Overwrite cksum1.txt", ['put', 'testsuite/checksum/cksum4.txt', 's3://%s/xyz/checksum/cksum1.txt' % bucket(1), '--no-encrypt' ])
411
+    os.system("rm testsuite/checksum/cksum4.txt")
412
+    os.system("attr -s md5sum -V $(md5sum testsuite/checksum/cksum1.txt | awk '{print $1}') testsuite/checksum/cksum1.txt > /dev/null")
413
+    test_s3cmd("Check MD5 with xattr", ['sync', 'testsuite/', 's3://%s/xyz/' % bucket(1), '--no-encrypt', '--check-md5', '--xattr=md5sum'],
414
+               must_find = [ "cksum1.txt" ])
415
+
407 416
 
408 417
 ## ====== Rename within S3
409 418
 test_s3cmd("Rename within S3", ['mv', '%s/xyz/etc/logo.png' % pbucket(1), '%s/xyz/etc2/Logo.PNG' % pbucket(1)],
... ...
@@ -26,6 +26,8 @@ import socket
26 26
 import shutil
27 27
 import tempfile
28 28
 import S3.Exceptions
29
+try: import xattr
30
+except: pass
29 31
 
30 32
 from copy import copy
31 33
 from optparse import OptionParser, Option, OptionValueError, IndentedHelpFormatter
... ...
@@ -1806,6 +1808,8 @@ def main():
1806 1806
     optparser.add_option(      "--skip-existing", dest="skip_existing", action="store_true", help="Skip over files that exist at the destination (only for [get] and [sync] commands).")
1807 1807
     optparser.add_option("-r", "--recursive", dest="recursive", action="store_true", help="Recursive upload, download or removal.")
1808 1808
     optparser.add_option(      "--check-md5", dest="check_md5", action="store_true", help="Check MD5 sums when comparing files for [sync]. (default)")
1809
+    if 'xattr' in sys.modules.keys():
1810
+        optparser.add_option  ("--xattr", dest="md5_xattr", metavar="ATTR", action="store", default=None, help="If possible, use extended file attribute ATTR from the user namespace containing the md5sum for the file, instead of calculating it [sync].")
1809 1811
     optparser.add_option(      "--no-check-md5", dest="check_md5", action="store_false", help="Do not check MD5 sums when comparing files for [sync]. Only size will be compared. May significantly speed up transfer but may also miss some changed files.")
1810 1812
     optparser.add_option("-P", "--acl-public", dest="acl_public", action="store_true", help="Store objects with ACL allowing read for anyone.")
1811 1813
     optparser.add_option(      "--acl-private", dest="acl_public", action="store_false", help="Store objects with default ACL allowing access for you only.")
... ...
@@ -1988,6 +1992,7 @@ def main():
1988 1988
     ## Special handling for tri-state options (True, False, None)
1989 1989
     cfg.update_option("enable", options.enable)
1990 1990
     cfg.update_option("acl_public", options.acl_public)
1991
+    cfg.update_option("md5_xattr", options.md5_xattr)
1991 1992
 
1992 1993
     ## Check multipart chunk constraints
1993 1994
     if cfg.multipart_chunk_size_mb < MultiPartUpload.MIN_CHUNK_SIZE_MB:
... ...
@@ -165,6 +165,9 @@ Check MD5 sums when comparing files for [sync]. (default)
165 165
 \fB\-\-no\-check\-md5\fR
166 166
 Do not check MD5 sums when comparing files for [sync]. Only size will be compared. May significantly speed up transfer but may also miss some changed files.
167 167
 .TP
168
+\fB-\-xattr\fR=ATTR
169
+If possible, use extended file attribute ATTR from the user namespace containing the md5sum for the file, instead of calculating it [sync].
170
+.TP
168 171
 \fB\-P\fR, \fB\-\-acl\-public\fR
169 172
 Store objects with ACL allowing read for anyone.
170 173
 .TP