diff --git a/darc/archive.py b/darc/archive.py index 2726a8e91..6b52f9da2 100644 --- a/darc/archive.py +++ b/darc/archive.py @@ -12,7 +12,7 @@ import xattr from .chunker import chunkify from .helpers import uid2user, user2uid, gid2group, group2gid, \ - Statistics, decode_dict + Statistics, decode_dict, st_mtime_ns ITEMS_BUFFER = 1024 * 1024 CHUNK_MIN = 1024 @@ -20,6 +20,7 @@ WINDOW_SIZE = 0xfff CHUNK_MASK = 0xffff utime_supports_fd = os.utime in getattr(os, 'supports_fd', {}) +has_mtime_ns = sys.version >= '3.3' has_lchmod = hasattr(os, 'lchmod') @@ -304,11 +305,11 @@ class Archive(object): elif has_lchmod: # Not available on Linux os.lchmod(path, item[b'mode']) if fd and utime_supports_fd: # Python >= 3.3 - os.utime(fd, (item[b'mtime'], item[b'mtime'])) + os.utime(fd, None, ns=(item[b'mtime'], item[b'mtime'])) elif utime_supports_fd: # Python >= 3.3 - os.utime(path, (item[b'mtime'], item[b'mtime']), follow_symlinks=False) + os.utime(path, None, ns=(item[b'mtime'], item[b'mtime']), follow_symlinks=False) elif not symlink: - os.utime(path, (item[b'mtime'], item[b'mtime'])) + os.utime(path, (item[b'mtime'] / 10**9, item[b'mtime'] / 10**9)) def verify_file(self, item, start, result, peek=None): if not item[b'chunks']: @@ -347,7 +348,7 @@ class Archive(object): b'mode': st.st_mode, b'uid': st.st_uid, b'user': uid2user(st.st_uid), b'gid': st.st_gid, b'group': gid2group(st.st_gid), - b'mtime': st.st_mtime, + b'mtime': st_mtime_ns(st), } if self.numeric_owner: item[b'user'] = item[b'group'] = None diff --git a/darc/archiver.py b/darc/archiver.py index 6412df833..dd4eae7fb 100644 --- a/darc/archiver.py +++ b/darc/archiver.py @@ -195,7 +195,7 @@ class Archiver(object): size = sum(size for _, size, _ in item[b'chunks']) except KeyError: pass - mtime = format_time(datetime.fromtimestamp(item[b'mtime'])) + mtime = format_time(datetime.fromtimestamp(item[b'mtime'] / 10**9)) if b'source' in item: if type == 'l': extra = ' -> %s' % item[b'source'] diff --git a/darc/cache.py b/darc/cache.py index d1931afa7..5ce2c40ce 100644 --- a/darc/cache.py +++ b/darc/cache.py @@ -6,7 +6,7 @@ import os from binascii import hexlify, unhexlify import shutil -from .helpers import get_cache_dir, decode_dict +from .helpers import get_cache_dir, decode_dict, st_mtime_ns from .hashindex import ChunkIndex @@ -190,7 +190,7 @@ class Cache(object): if self.files is None: self._read_files() entry = self.files.get(path_hash) - if (entry and entry[3] == st.st_mtime + if (entry and entry[3] == st_mtime_ns(st) and entry[2] == st.st_size and entry[1] == st.st_ino): # reset entry age self.files[path_hash][0] = 0 @@ -200,6 +200,6 @@ class Cache(object): def memorize_file(self, path_hash, st, ids): # Entry: Age, inode, size, mtime, chunk ids - self.files[path_hash] = 0, st.st_ino, st.st_size, st.st_mtime, ids - self._newest_mtime = max(self._newest_mtime, st.st_mtime) - + mtime_ns = st_mtime_ns(st) + self.files[path_hash] = 0, st.st_ino, st.st_size, mtime_ns, ids + self._newest_mtime = max(self._newest_mtime, mtime_ns) diff --git a/darc/helpers.py b/darc/helpers.py index edaa6bc4f..10b82ba3d 100644 --- a/darc/helpers.py +++ b/darc/helpers.py @@ -375,4 +375,12 @@ def decode_dict(d, keys, encoding='utf-8', errors='surrogateescape'): def remove_surrogates(s, errors='replace'): - return s.encode('utf-8', errors).decode('utf-8') \ No newline at end of file + return s.encode('utf-8', errors).decode('utf-8') + + +if sys.version < '3.3': + def st_mtime_ns(st): + return int(st.st_mtime * 10**9) +else: + def st_mtime_ns(st): + return st.st_mtime_ns diff --git a/darc/test.py b/darc/test.py index a59f18c51..6be147fcd 100644 --- a/darc/test.py +++ b/darc/test.py @@ -16,6 +16,7 @@ from .key import suite as KeySuite from .store import Store, suite as StoreSuite from .remote import Store, suite as RemoteStoreSuite +has_mtime_ns = sys.version >= '3.3' utime_supports_fd = os.utime in getattr(os, 'supports_fd', {}) @@ -89,12 +90,13 @@ class Test(unittest.TestCase): s2 = os.lstat(path2) attrs = ['st_mode', 'st_uid', 'st_gid', 'st_rdev'] if not os.path.islink(path1) or utime_supports_fd: - attrs.append('st_mtime') + attrs.append('st_mtime_ns' if has_mtime_ns else 'st_mtime') d1 = [filename] + [getattr(s1, a) for a in attrs] d2 = [filename] + [getattr(s2, a) for a in attrs] - if(len(d1) == 6): - d1[-1] = int(d1[-1]) - d2[-1] = int(d2[-1]) + # 'st_mtime precision is limited' + if attrs[-1] == 'st_mtime': + d1[-1] = round(d1[-1], 4) + d2[-1] = round(d2[-1], 4) d1.append(self.get_xattrs(path1)) d2.append(self.get_xattrs(path2)) self.assertEqual(d1, d2)