]> arthur.barton.de Git - bup.git/commitdiff
Replace os.*stat() with xstat.*stat(); use integer ns for all fs times.
authorRob Browning <rlb@defaultvalue.org>
Sun, 27 Mar 2011 17:01:45 +0000 (12:01 -0500)
committerAvery Pennarun <apenwarr@gmail.com>
Sun, 8 May 2011 07:39:14 +0000 (03:39 -0400)
Replace all calls of the os.*stat() functions with calls to the xstat
equivalents.  This should leave bup with the xstat stat representation
(and integer ns timestamps) everywhere.

Remove FSTime, and add a few xstat conversion functions to replace the
bits we still want: timespec_to_nsecs(), nsecs_to_timespec(),
fstime_floor_secs(), fstime_to_timespec().

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
cmd/midx-cmd.py
cmd/xstat-cmd.py
lib/bup/git.py
lib/bup/index.py
lib/bup/metadata.py
lib/bup/t/txstat.py
lib/bup/xstat.py
t/test-meta.sh

index 38a5fdfca500639894111181edeb3a1007aea38a..bbbee079f6097b6083f1ba60571aa8ee3499a04d 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/env python
 import sys, math, struct, glob, resource
 import tempfile
-from bup import options, git, midx, _helpers
+from bup import options, git, midx, _helpers, xstat
 from bup.helpers import *
 
 PAGE_SIZE=4096
@@ -162,7 +162,7 @@ def do_midx_dir(path):
                     
         # sort the biggest+newest midxes first, so that we can eliminate
         # smaller (or older) redundant ones that come later in the list
-        midxs.sort(key=lambda ix: (-sizes[ix], -os.stat(ix).st_mtime))
+        midxs.sort(key=lambda ix: (-sizes[ix], -xstat.stat(ix).st_mtime))
         
         for mname in midxs:
             any = 0
index 9d1f57530ff704ccd206e9d2b5635a7f74301771..5523208d2116398ee514cc93c802e67c16a5197c 100755 (executable)
@@ -9,7 +9,9 @@ from bup.helpers import handle_ctrl_c, saved_errors, add_error, log
 
 
 def fstimestr(fstime):
-    (s, ns) = fstime.secs_nsecs()
+    (s, ns) = xstat.fstime_to_timespec(fstime)
+    if(s < 0):
+        s += 1
     if ns == 0:
         return '%d' % s
     else:
index 233a2ee70e7bd6687679495d114d8e35aecabb89..eacf407a57b5e68cdc65f867a968eee3bd495c7c 100644 (file)
@@ -4,7 +4,7 @@ interact with the Git data structures.
 """
 import os, sys, zlib, time, subprocess, struct, stat, re, tempfile, glob
 from bup.helpers import *
-from bup import _helpers, path, midx, bloom
+from bup import _helpers, path, midx, bloom, xstat
 
 max_pack_size = 1000*1000*1000  # larger packs will slow down pruning
 max_pack_objects = 200*1000  # cache memory usage is about 83 bytes per object
@@ -409,7 +409,7 @@ class PackIdxList:
                         else:
                             midxl.append(mx)
                 midxl.sort(key=lambda ix:
-                           (-len(ix), -os.stat(ix.name).st_mtime))
+                           (-len(ix), -xstat.stat(ix.name).st_mtime))
                 for ix in midxl:
                     any_needed = False
                     for sub in ix.idxnames:
index 0007bbc02c31023fb309446219447b20eaca79c2..460a52f18de76f5bb60a2ccf45329a83e03f8e14 100644 (file)
@@ -1,4 +1,5 @@
 import os, stat, struct, tempfile
+from bup import xstat
 from bup.helpers import *
 
 EMPTY_SHA = '\0'*20
@@ -96,18 +97,18 @@ class Entry:
         old = (self.dev, self.ctime, self.mtime,
                self.uid, self.gid, self.size, self.flags & IX_EXISTS)
         new = (st.st_dev,
-               int(st.st_ctime.approx_secs()),
-               int(st.st_mtime.approx_secs()),
+               xstat.fstime_floor_secs(st.st_ctime),
+               xstat.fstime_floor_secs(st.st_mtime),
                st.st_uid, st.st_gid, st.st_size, IX_EXISTS)
         self.dev = st.st_dev
-        self.ctime = int(st.st_ctime.approx_secs())
-        self.mtime = int(st.st_mtime.approx_secs())
+        self.ctime = xstat.fstime_floor_secs(st.st_ctime)
+        self.mtime = xstat.fstime_floor_secs(st.st_mtime)
         self.uid = st.st_uid
         self.gid = st.st_gid
         self.size = st.st_size
         self.mode = st.st_mode
         self.flags |= IX_EXISTS
-        if int(st.st_ctime.approx_secs()) >= tstart or old != new \
+        if xstat.fstime_floor_secs(st.st_ctime) >= tstart or old != new \
               or self.sha == EMPTY_SHA or not self.gitmode:
             self.invalidate()
         self._fixup()
@@ -410,8 +411,8 @@ class Writer:
             isdir = stat.S_ISDIR(st.st_mode)
             assert(isdir == endswith)
             e = NewEntry(basename, name, st.st_dev,
-                         int(st.st_ctime.approx_secs()),
-                         int(st.st_mtime.approx_secs()),
+                         xstat.fstime_floor_secs(st.st_ctime),
+                         xstat.fstime_floor_secs(st.st_mtime),
                          st.st_uid, st.st_gid,
                          st.st_size, st.st_mode, gitmode, sha, flags,
                          0, 0)
index 6dae38b83bc08404b5952ce87e2d5c85c150ecb8..aecf740a13c3507559b4bf56b638acde901ffb50 100644 (file)
@@ -6,10 +6,10 @@
 # Public License as described in the bup LICENSE file.
 import errno, os, sys, stat, pwd, grp, struct, re
 from cStringIO import StringIO
-from bup import vint
+from bup import vint, xstat
 from bup.drecurse import recursive_dirlist
 from bup.helpers import add_error, mkdirp, log
-from bup.xstat import utime, lutime, lstat, FSTime
+from bup.xstat import utime, lutime
 import bup._helpers as _helpers
 
 try:
@@ -201,9 +201,9 @@ class Metadata:
             add_error("no group name for id %s '%s'" % (st.st_gid, path))
 
     def _encode_common(self):
-        atime = self.atime.to_timespec()
-        mtime = self.mtime.to_timespec()
-        ctime = self.ctime.to_timespec()
+        atime = xstat.nsecs_to_timespec(self.atime)
+        mtime = xstat.nsecs_to_timespec(self.mtime)
+        ctime = xstat.nsecs_to_timespec(self.ctime)
         result = vint.pack('VVsVsVvVvVvV',
                            self.mode,
                            self.uid,
@@ -233,16 +233,16 @@ class Metadata:
          mtime_ns,
          self.ctime,
          ctime_ns) = vint.unpack('VVsVsVvVvVvV', data)
-        self.atime = FSTime.from_timespec((self.atime, atime_ns))
-        self.mtime = FSTime.from_timespec((self.mtime, mtime_ns))
-        self.ctime = FSTime.from_timespec((self.ctime, ctime_ns))
+        self.atime = xstat.timespec_to_nsecs((self.atime, atime_ns))
+        self.mtime = xstat.timespec_to_nsecs((self.mtime, mtime_ns))
+        self.ctime = xstat.timespec_to_nsecs((self.ctime, ctime_ns))
 
     def _create_via_common_rec(self, path, create_symlinks=True):
         # If the path already exists and is a dir, try rmdir.
         # If the path already exists and is anything else, try unlink.
         st = None
         try:
-            st = lstat(path)
+            st = xstat.lstat(path)
         except OSError, e:
             if e.errno != errno.ENOENT:
                 raise
@@ -611,7 +611,7 @@ class Metadata:
 def from_path(path, statinfo=None, archive_path=None, save_symlinks=True):
     result = Metadata()
     result.path = archive_path
-    st = statinfo or lstat(path)
+    st = statinfo or xstat.lstat(path)
     result._add_common(path, st)
     if save_symlinks:
         result._add_symlink_target(path, st)
index a43da6683c58432bc39ddcc6f91bb83ceb578bfc..30b8284c68fe0b4d7ebaf9a3a79599b3e6c575a2 100644 (file)
@@ -1,46 +1,39 @@
 import math
 from wvtest import *
 import bup._helpers as _helpers
-from bup.xstat import FSTime
+from bup import xstat
 
 @wvtest
 def test_fstime():
-    def approx_eq(x, y):
-        return math.fabs(x - y) < 1 / 10e8
-    def ts_eq_ish(x, y):
-        return approx_eq(x[0], y[0]) and approx_eq(x[1], y[1])
-    def fst_eq_ish(x, y):
-        return approx_eq(x.approx_secs(), y.approx_secs())
-    def s_ns_eq_ish(fst, s, ns):
-        (fst_s, fst_ns) = fst.secs_nsecs()
-        return approx_eq(fst_s, s) and approx_eq(fst_ns, ns)
-    from_secs = FSTime.from_secs
-    from_ts = FSTime.from_timespec
-    WVPASS(from_secs(0) == from_secs(0))
-    WVPASS(from_secs(0) < from_secs(1))
-    WVPASS(from_secs(-1) < from_secs(1))
-    WVPASS(from_secs(1) > from_secs(0))
-    WVPASS(from_secs(1) > from_secs(-1))
+    WVPASSEQ(xstat.timespec_to_nsecs((0, 0)), 0)
+    WVPASSEQ(xstat.timespec_to_nsecs((1, 0)), 10**9)
+    WVPASSEQ(xstat.timespec_to_nsecs((0, 10**9 / 2)), 500000000)
+    WVPASSEQ(xstat.timespec_to_nsecs((1, 10**9 / 2)), 1500000000)
+    WVPASSEQ(xstat.timespec_to_nsecs((-1, 0)), -10**9)
+    WVPASSEQ(xstat.timespec_to_nsecs((-1, 10**9 / 2)), -500000000)
+    WVPASSEQ(xstat.timespec_to_nsecs((-2, 10**9 / 2)), -1500000000)
+    WVEXCEPT(Exception, xstat.timespec_to_nsecs, (0, -1))
+    WVPASSEQ(type(xstat.timespec_to_nsecs((2, 22222222))), type(0))
+    WVPASSEQ(type(xstat.timespec_to_nsecs((-2, 22222222))), type(0))
 
-    WVPASS(fst_eq_ish(from_secs(0), from_ts((0, 0))))
-    WVPASS(fst_eq_ish(from_secs(1), from_ts((1, 0))))
-    WVPASS(fst_eq_ish(from_secs(-1), from_ts((-1, 0))))
-    WVPASS(fst_eq_ish(from_secs(-0.5), from_ts((-1, 10**9 / 2))))
-    WVPASS(fst_eq_ish(from_secs(-1.5), from_ts((-2, 10**9 / 2))))
-    WVPASS(fst_eq_ish(from_secs(1 / 10e8), from_ts((0, 1))))
-    WVPASS(fst_eq_ish(from_secs(-1 / 10e8), from_ts((-1, 10**9 - 1))))
+    WVPASSEQ(xstat.nsecs_to_timespec(0), (0, 0))
+    WVPASSEQ(xstat.nsecs_to_timespec(10**9), (1, 0))
+    WVPASSEQ(xstat.nsecs_to_timespec(500000000), (0, 10**9 / 2))
+    WVPASSEQ(xstat.nsecs_to_timespec(1500000000), (1, 10**9 / 2))
+    WVPASSEQ(xstat.nsecs_to_timespec(-10**9), (-1, 0))
+    WVPASSEQ(xstat.nsecs_to_timespec(-500000000), (-1, 10**9 / 2))
+    WVPASSEQ(xstat.nsecs_to_timespec(-1500000000), (-2, 10**9 / 2))
+    x = xstat.nsecs_to_timespec(1977777778)
+    WVPASSEQ(type(x[0]), type(0))
+    WVPASSEQ(type(x[1]), type(0))
+    x = xstat.nsecs_to_timespec(-1977777778)
+    WVPASSEQ(type(x[0]), type(0))
+    WVPASSEQ(type(x[1]), type(0))
 
-    WVPASS(ts_eq_ish(from_secs(0).to_timespec(), (0, 0)))
-    WVPASS(ts_eq_ish(from_secs(1).to_timespec(), (1, 0)))
-    WVPASS(ts_eq_ish(from_secs(-1).to_timespec(), (-1, 0)))
-    WVPASS(ts_eq_ish(from_secs(-0.5).to_timespec(), (-1, 10**9 / 2)))
-    WVPASS(ts_eq_ish(from_secs(-1.5).to_timespec(), (-2, 10**9 / 2)))
-    WVPASS(ts_eq_ish(from_secs(1 / 10e8).to_timespec(), (0, 1)))
-    WVPASS(ts_eq_ish(from_secs(-1 / 10e8).to_timespec(), (-1, 10**9 - 1)))
-
-    WVPASS(s_ns_eq_ish(from_secs(0), 0, 0))
-    WVPASS(s_ns_eq_ish(from_secs(1), 1, 0))
-    WVPASS(s_ns_eq_ish(from_secs(-1), -1, 0))
-    WVPASS(s_ns_eq_ish(from_secs(-0.5), 0, - 10**9 / 2))
-    WVPASS(s_ns_eq_ish(from_secs(-1.5), -1, - 10**9 / 2))
-    WVPASS(s_ns_eq_ish(from_secs(-1 / 10e8), 0, -1))
+    WVPASSEQ(xstat.fstime_floor_secs(0), 0)
+    WVPASSEQ(xstat.fstime_floor_secs(10**9 / 2), 0)
+    WVPASSEQ(xstat.fstime_floor_secs(10**9), 1)
+    WVPASSEQ(xstat.fstime_floor_secs(-10**9 / 2), -1)
+    WVPASSEQ(xstat.fstime_floor_secs(-10**9), -1)
+    WVPASSEQ(type(xstat.fstime_floor_secs(10**9 / 2)), type(0))
+    WVPASSEQ(type(xstat.fstime_floor_secs(-10**9 / 2)), type(0))
index e3044bebfd505a9a9573dc73f9e454253e9802fa..bad1c274be77506f9d2807b0eb837adbc3e9cbe5 100644 (file)
@@ -9,72 +9,52 @@ except AttributeError, e:
     _have_utimensat = False
 
 
-class FSTime:
-    # Class to represent filesystem timestamps.  Use integer
-    # nanoseconds on platforms where we have the higher resolution
-    # lstat.  Use the native python stat representation (floating
-    # point seconds) otherwise.
-
-    def __cmp__(self, x):
-        return self._value.__cmp__(x._value)
-        
-    def __repr__(self):
-        return 'FSTime(%d)' % self._value
-        
-    def to_timespec(self):
-        """Return (s, ns) where ns is always non-negative
-        and t = s + ns / 10e8""" # metadata record rep (and libc rep)
-        s_ns = self.secs_nsecs()
-        if s_ns[0] > 0 or s_ns[1] >= 0:
-            return s_ns
-        return (s_ns[0] - 1, 10**9 + s_ns[1]) # ns is negative
+def timespec_to_nsecs((ts_s, ts_ns)):
+    # c.f. _helpers.c: timespec_vals_to_py_ns()
+    if ts_ns < 0 or ts_ns > 999999999:
+        raise Exception('invalid timespec nsec value')
+    return ts_s * 10**9 + ts_ns
 
-    @staticmethod
-    def from_secs(secs):
-        ts = FSTime()
-        ts._value = int(round(secs * 10**9))
-        return ts
 
-    @staticmethod
-    def from_timespec(timespec):
-        ts = FSTime()
-        ts._value = timespec[0] * 10**9 + timespec[1]
-        return ts
+def nsecs_to_timespec(ns):
+    """Return (s, ns) where ns is always non-negative
+    and t = s + ns / 10e8""" # metadata record rep (and libc rep)
+    ns = int(ns)
+    return (ns / 10**9, ns % 10**9)
+
+
+def fstime_floor_secs(ns):
+    """Return largest integer not greater than ns / 10e8."""
+    return int(ns) / 10**9;
 
-    def approx_secs(self):
-        return self._value / 10e8;
 
-    def secs_nsecs(self):
-        "Return a (s, ns) pair: -1.5s -> (-1, -10**9 / 2)."
-        if self._value >= 0:
-            return (self._value / 10**9, self._value % 10**9)
-        abs_val = -self._value
-        return (- (abs_val / 10**9), - (abs_val % 10**9))
+def fstime_to_timespec(ns):
+    return nsecs_to_timespec(ns)
 
 
 if _have_utimensat:
     def lutime(path, times):
-        atime = times[0].to_timespec()
-        mtime = times[1].to_timespec()
+        atime = nsecs_to_timespec(times[0])
+        mtime = nsecs_to_timespec(times[1])
         return _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime),
                                   _helpers.AT_SYMLINK_NOFOLLOW)
     def utime(path, times):
-        atime = times[0].to_timespec()
-        mtime = times[1].to_timespec()
+        atime = nsecs_to_timespec(times[0])
+        mtime = nsecs_to_timespec(times[1])
         return _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime), 0)
 else:
     def lutime(path, times):
         return None
 
     def utime(path, times):
-        atime = times[0].approx_secs()
-        mtime = times[1].approx_secs()
+        atime = fstime_floor_secs(times[0])
+        mtime = fstime_floor_secs(times[1])
         os.utime(path, (atime, mtime))
 
 
 class stat_result:
     @staticmethod
-    def from_stat_rep(st):
+    def from_xstat_rep(st):
         result = stat_result()
         (result.st_mode,
          result.st_ino,
@@ -87,19 +67,19 @@ class stat_result:
          result.st_atime,
          result.st_mtime,
          result.st_ctime) = st
-        result.st_atime = FSTime.from_timespec(result.st_atime)
-        result.st_mtime = FSTime.from_timespec(result.st_mtime)
-        result.st_ctime = FSTime.from_timespec(result.st_ctime)
+        result.st_atime = timespec_to_nsecs(result.st_atime)
+        result.st_mtime = timespec_to_nsecs(result.st_mtime)
+        result.st_ctime = timespec_to_nsecs(result.st_ctime)
         return result
 
 
 def stat(path):
-    return stat_result.from_stat_rep(_helpers.stat(path))
+    return stat_result.from_xstat_rep(_helpers.stat(path))
 
 
 def fstat(path):
-    return stat_result.from_stat_rep(_helpers.fstat(path))
+    return stat_result.from_xstat_rep(_helpers.fstat(path))
 
 
 def lstat(path):
-    return stat_result.from_stat_rep(_helpers.lstat(path))
+    return stat_result.from_xstat_rep(_helpers.lstat(path))
index 9e85cfc6c4f22050baa5ff874fffa70845b1233c..830e2cf393fe3e6e577c5bb6f024defe2b567459 100755 (executable)
@@ -122,10 +122,10 @@ if actually-root; then
             mkdir testfs/src/foo
             touch testfs/src/bar
             PYTHONPATH="$TOP/lib" \
-                python -c "from bup.xstat import lutime, FSTime; \
-                x = FSTime.from_secs(42);\
-                   lutime('testfs/src/foo', (x, x));\
-                   lutime('testfs/src/bar', (x, x));"
+                python -c "from bup import xstat; \
+                x = xstat.timespec_to_nsecs((42, 0));\
+                   xstat.lutime('testfs/src/foo', (x, x));\
+                   xstat.lutime('testfs/src/bar', (x, x));"
             cd testfs
             WVPASS bup meta -v --create --recurse --file src.meta src
             bup meta -tvf src.meta