X-Git-Url: https://arthur.barton.de/gitweb/?a=blobdiff_plain;f=lib%2Fbup%2Findex.py;h=4dffd3a3a1180bc5647a2cbfdf28f845b82956ca;hb=d4100c81448484b8ea222fb78757bf003e181cf1;hp=9c81d9c8455786db8f3aae94bbdd073e87835536;hpb=38ab72c98ea162df2c8c2312cf48c889c9e0d3f8;p=bup.git diff --git a/lib/bup/index.py b/lib/bup/index.py index 9c81d9c..4dffd3a 100644 --- a/lib/bup/index.py +++ b/lib/bup/index.py @@ -1,14 +1,17 @@ -import errno, metadata, os, stat, struct, tempfile -from bup import xstat -from bup._helpers import UINT_MAX +from __future__ import absolute_import +import errno, os, stat, struct, tempfile + +from bup import metadata, xstat +from bup._helpers import UINT_MAX, bytescmp +from bup.compat import range from bup.helpers import (add_error, log, merge_iter, mmap_readwrite, progress, qprogress, resolve_parent, slashappend) EMPTY_SHA = '\0'*20 FAKE_SHA = '\x01'*20 -INDEX_HDR = 'BUPI\0\0\0\6' +INDEX_HDR = 'BUPI\0\0\0\7' # Time values are handled as integer nanoseconds since the epoch in # memory, but are written as xstat/metadata timespecs. This behavior @@ -17,7 +20,21 @@ INDEX_HDR = 'BUPI\0\0\0\6' # Record times (mtime, ctime, atime) as xstat/metadata timespecs, and # store all of the times in the index so they won't interfere with the # forthcoming metadata cache. -INDEX_SIG = '!QQQqQqQqQQII20sHIIQ' +INDEX_SIG = ('!' + 'Q' # dev + 'Q' # ino + 'Q' # nlink + 'qQ' # ctime_s, ctime_ns + 'qQ' # mtime_s, mtime_ns + 'qQ' # atime_s, atime_ns + 'Q' # size + 'I' # mode + 'I' # gitmode + '20s' # sha + 'H' # flags + 'Q' # children_ofs + 'I' # children_n + 'Q') # meta_ofs ENTLEN = struct.calcsize(INDEX_SIG) FOOTER_SIG = '!Q' @@ -181,13 +198,34 @@ class Entry: log('pack error: %s (%r)\n' % (e, self)) raise - def from_stat(self, st, meta_ofs, tstart, check_device=True): - old = (self.dev if check_device else 0, - self.ino, self.nlink, self.ctime, self.mtime, - self.size, self.flags & IX_EXISTS) - new = (st.st_dev if check_device else 0, - st.st_ino, st.st_nlink, st.st_ctime, st.st_mtime, - st.st_size, IX_EXISTS) + def stale(self, st, tstart, check_device=True): + if self.size != st.st_size: + return True + if self.mtime != st.st_mtime: + return True + if self.sha == EMPTY_SHA: + return True + if not self.gitmode: + return True + if self.ctime != st.st_ctime: + return True + if self.ino != st.st_ino: + return True + if self.nlink != st.st_nlink: + return True + if not (self.flags & IX_EXISTS): + return True + if check_device and (self.dev != st.st_dev): + return True + # Check that the ctime's "second" is at or after tstart's. + ctime_sec_in_ns = xstat.fstime_floor_secs(st.st_ctime) * 10**9 + if ctime_sec_in_ns >= tstart: + return True + return False + + def update_from_stat(self, st, meta_ofs): + # Should only be called when the entry is stale(), and + # invalidate() should almost certainly be called afterward. self.dev = st.st_dev self.ino = st.st_ino self.nlink = st.st_nlink @@ -198,13 +236,8 @@ class Entry: self.mode = st.st_mode self.flags |= IX_EXISTS self.meta_ofs = meta_ofs - # Check that the ctime's "second" is at or after tstart's. - ctime_sec_in_ns = xstat.fstime_floor_secs(st.st_ctime) * 10**9 - if ctime_sec_in_ns >= tstart or old != new \ - or self.sha == EMPTY_SHA or not self.gitmode: - self.invalidate() self._fixup() - + def _fixup(self): self.mtime = self._fixup_time(self.mtime) self.ctime = self._fixup_time(self.ctime) @@ -249,10 +282,36 @@ class Entry: def is_fake(self): return not self.ctime - def __cmp__(a, b): - return (cmp(b.name, a.name) - or cmp(a.is_valid(), b.is_valid()) - or cmp(a.is_fake(), b.is_fake())) + def _cmp(self, other): + # Note reversed name ordering + bc = bytescmp(other.name, self.name) + if bc != 0: + return bc + vc = self.is_valid() - other.is_valid() + if vc != 0: + return vc + fc = self.is_fake() - other.is_fake() + if fc != 0: + return fc + return 0 + + def __eq__(self, other): + return self._cmp(other) == 0 + + def __ne__(): + return self._cmp(other) != 0 + + def __lt__(self, other): + return self._cmp(other) < 0 + + def __gt__(self, other): + return self._cmp(other) > 0 + + def __le__(): + return self._cmp(other) <= 0 + + def __ge__(): + return self._cmp(other) >= 0 def write(self, f): f.write(self.basename + '\0' + self.packed()) @@ -325,7 +384,7 @@ class ExistingEntry(Entry): ofs = self.children_ofs assert(ofs <= len(self._m)) assert(self.children_n <= UINT_MAX) # i.e. python struct 'I' - for i in xrange(self.children_n): + for i in range(self.children_n): eon = self._m.find('\0', ofs) assert(eon >= 0) assert(eon >= ofs)