]> arthur.barton.de Git - bup.git/commitdiff
Don't allow negative ns in metadata timestamps; normalize on read/write.
authorRob Browning <rlb@defaultvalue.org>
Sat, 25 Sep 2010 17:27:45 +0000 (12:27 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sat, 25 Sep 2010 17:27:45 +0000 (12:27 -0500)
Read/write metadata timestamps as (vint-sec, vuint-ns) rather than
(vint-sec, vint-ns), and specify that ns must be > 0 and < 10**9.
Also specify that t = 0 indicates 1970-01-01 00:00:00.

Add metadata._normalize_ts() and normalize timestamps before write.
Also normalize timestamps when reading if necessary (and warn).

lib/bup/metadata.py
lib/bup/t/tmetadata.py

index cca82234d65fd6c70e463628e5151cecd8a851b1..3b9dc5391682c8ecc0901ffb7394de7dd01efbb4 100644 (file)
@@ -129,6 +129,18 @@ def _clean_up_extract_path(p):
         return result
 
 
+def _normalize_ts(stamp):
+    # For the purposes of normalization, t = s + ns.
+    s = stamp[0]
+    ns = stamp[1]
+    if ns < 0 or ns >= 10**9:
+        t = (s * 10**9) + ns
+        if t == 0:
+            return (0, 0)
+        return ((t / 10**9), t % 10**9)
+    return stamp
+
+
 # These tags are currently conceptually private to Metadata, and they
 # must be unique, and must *never* be changed.
 _rec_tag_end = 0
@@ -148,6 +160,9 @@ class Metadata:
 
     ## Common records
 
+    # Timestamps are (sec, ns), relative to 1970-01-01 00:00:00, ns
+    # must be non-negative and < 10**9.
+
     def _add_common(self, path, st):
         self.mode = st.st_mode
         self.uid = st.st_uid
@@ -160,19 +175,22 @@ class Metadata:
         self.group = grp.getgrgid(st.st_gid)[0]
 
     def _encode_common(self):
-        result = vint.pack('VVsVsVvvvvvv',
+        atime = _normalize_ts(self.atime)
+        mtime = _normalize_ts(self.mtime)
+        ctime = _normalize_ts(self.ctime)
+        result = vint.pack('VVsVsVvVvVvV',
                            self.mode,
                            self.uid,
                            self.owner,
                            self.gid,
                            self.group,
                            self.rdev,
-                           self.atime[0],
-                           self.atime[1],
-                           self.mtime[0],
-                           self.mtime[1],
-                           self.ctime[0],
-                           self.ctime[1])
+                           atime[0],
+                           atime[1],
+                           mtime[0],
+                           mtime[1],
+                           ctime[0],
+                           ctime[1])
         return result
 
     def _load_common_rec(self, port):
@@ -188,10 +206,22 @@ class Metadata:
          self.mtime,
          mtime_ns,
          self.ctime,
-         ctime_ns) = vint.unpack('VVsVsVvvvvvv', data)
+         ctime_ns) = vint.unpack('VVsVsVvVvVvV', data)
         self.atime = (self.atime, atime_ns)
         self.mtime = (self.mtime, mtime_ns)
         self.ctime = (self.ctime, ctime_ns)
+        if self.atime[1] >= 10**9:
+            path = ' for ' + self.path if self.path else ''
+            log('bup: warning - normalizing bad atime%s\n' % (path))
+            self.atime = _normalize_ts(self.atime)
+        if self.mtime[1] >= 10**9:
+            path = ' for ' + self.path if self.path else ''
+            log('bup: warning - normalizing bad mtime%s\n' % (path))
+            self.mtime = _normalize_ts(self.mtime)
+        if self.ctime[1] >= 10**9:
+            path = ' for ' + self.path if self.path else ''
+            log('bup: warning - normalizing bad ctime%s\n' % (path))
+            self.ctime = _normalize_ts(self.ctime)
 
     def _create_via_common_rec(self, path, create_symlinks=True):
         if stat.S_ISREG(self.mode):
index d6cd0563974a1c376eb8e54a648fa858f40507c3..2bbc529ddb9df9455c86037e076459340949cb33 100644 (file)
@@ -2,6 +2,22 @@ from bup import metadata
 from wvtest import *
 
 
+@wvtest
+def test__normalize_ts():
+    normalize = metadata._normalize_ts
+    bns = 10**9
+    for ts in ((0, 0), (-1, 0), (0, bns - 1), (-1, bns - 1)):
+        WVPASSEQ(normalize(ts), ts)
+    WVPASSEQ(normalize((0, -1)), (-1, bns - 1))
+    WVPASSEQ(normalize((-1, -1)), (-2, bns - 1))
+    WVPASSEQ(normalize((0, bns)), (1, 0))
+    WVPASSEQ(normalize((0, bns + 1)), (1, 1))
+    WVPASSEQ(normalize((0, -bns)), (-1, 0))
+    WVPASSEQ(normalize((0, -(bns + 1))), (-2, bns - 1))
+    WVPASSEQ(normalize((0, 3 * bns)), (3, 0))
+    WVPASSEQ(normalize((0, -3 * bns)), (-3, 0))
+
+
 @wvtest
 def test_clean_up_archive_path():
     cleanup = metadata._clean_up_path_for_archive