Jelajahi Sumber

Detect and abort if repository is older than the cache

Jonas Borgström 11 tahun lalu
induk
melakukan
5fa5380f0f
4 mengubah file dengan 27 tambahan dan 1 penghapusan
  1. 8 0
      CHANGES
  2. 3 0
      attic/archiver.py
  3. 11 1
      attic/cache.py
  4. 5 0
      attic/helpers.py

+ 8 - 0
CHANGES

@@ -4,6 +4,14 @@ Attic Changelog
 Here you can see the full list of changes between each Attic release.
 
 
+Version 0.8
+-----------
+
+(feature release, released on X)
+
+- Detect and abort if repository is older than the cache
+
+
 Version 0.7
 -----------
 

+ 3 - 0
attic/archiver.py

@@ -492,6 +492,9 @@ def main():
     except Archive.DoesNotExist as e:
         archiver.print_error('Error: Archive "%s" does not exist', e)
         exit_code = 1
+    except Cache.RepositoryReplay:
+        archiver.print_error('Cache is newer than repository, refusing to continue')
+        exit_code = 1
     except ConnectionClosed:
         archiver.print_error('Connection closed by remote host')
         exit_code = 1

+ 11 - 1
attic/cache.py

@@ -14,7 +14,12 @@ class Cache(object):
     """Client Side cache
     """
 
+    class RepositoryReplay(Exception):
+        """
+        """
+
     def __init__(self, repository, key, manifest):
+        self.timestamp = None
         self.txn_active = False
         self.repository = repository
         self.key = key
@@ -24,6 +29,9 @@ class Cache(object):
             self.create()
         self.open()
         if self.manifest.id != self.manifest_id:
+            # If repository is older than the cache something fishy is going on
+            if self.timestamp and self.timestamp > manifest.timestamp:
+                raise self.RepositoryReplay()
             self.sync()
             self.commit()
 
@@ -31,7 +39,7 @@ class Cache(object):
         self.close()
 
     def create(self):
-        """Create a new empty repository at `path`
+        """Create a new empty cache at `path`
         """
         os.makedirs(self.path)
         with open(os.path.join(self.path, 'README'), 'w') as fd:
@@ -59,6 +67,7 @@ class Cache(object):
             raise Exception('%s Does not look like an Attic cache')
         self.id = self.config.get('cache', 'repository')
         self.manifest_id = unhexlify(self.config.get('cache', 'manifest'))
+        self.timestamp = self.config.get('cache', 'timestamp', fallback=None)
         self.chunks = ChunkIndex(os.path.join(self.path, 'chunks').encode('utf-8'))
         self.files = None
 
@@ -103,6 +112,7 @@ class Cache(object):
                     if item[1][0] < 10 and item[1][3] < self._newest_mtime:
                         msgpack.pack(item, fd)
         self.config.set('cache', 'manifest', hexlify(self.manifest.id).decode('ascii'))
+        self.config.set('cache', 'timestamp', self.manifest.timestamp)
         with open(os.path.join(self.path, 'config'), 'w') as fd:
             self.config.write(fd)
         self.chunks.flush()

+ 5 - 0
attic/helpers.py

@@ -34,13 +34,18 @@ class Manifest:
         if not m.get(b'version') == 1:
             raise ValueError('Invalid manifest version')
         manifest.archives = dict((k.decode('utf-8'), v) for k,v in m[b'archives'].items())
+        manifest.timestamp = m.get(b'timestamp')
+        if manifest.timestamp:
+            manifest.timestamp = manifest.timestamp.decode('ascii')
         manifest.config = m[b'config']
         return manifest, key
 
     def write(self):
+        self.timestamp = datetime.utcnow().isoformat()
         data = msgpack.packb({
             'version': 1,
             'archives': self.archives,
+            'timestamp': self.timestamp,
             'config': self.config,
         })
         self.id = self.key.id_hash(data)