]> arthur.barton.de Git - bup.git/commitdiff
Replace _helpers.utimensat() with utime() and lutime().
authorRob Browning <rlb@defaultvalue.org>
Wed, 1 Jun 2011 00:49:33 +0000 (19:49 -0500)
committerAvery Pennarun <apenwarr@gmail.com>
Thu, 9 Jun 2011 03:12:24 +0000 (23:12 -0400)
Rework utimensat() handling in preparation for the addition of
utimes/lutimes based fallbacks.

Publish lutime() and utime() at the Python level from _helpers.c
rather than utimensat() itself.

Drop the _have_utimensat tests in favor of testing xstat.lutime which
will be false when xstat.lutime() is not available.

Move bup_utimensat() Python argument parsing to
bup_parse_xutime_args() and use it to implement bup_utime_ns() and
bup_lutime_ns().  This argument parsing will eventually be shared by
the utimes/lutimes based fallbacks.

Remove _helpers.AT_FDCWD and _helpers.AT_SYMLINK_NOFOLLOW since
utimensat is no longer published on the Python side.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
cmd/xstat-cmd.py
lib/bup/_helpers.c
lib/bup/metadata.py
lib/bup/t/txstat.py
lib/bup/xstat.py

index f540004931fa151edda6b32cbb5f4ae607929316..99b9f192d583578f059c2e92d468ee2b53908852 100755 (executable)
@@ -104,12 +104,12 @@ for path in remainder:
         # symlink.  Thus, the mtime/atime of a symlink is meaningless,
         # so let's not report it.  (That way scripts comparing
         # before/after won't trigger.)
-        if xstat._have_utimensat or not stat.S_ISLNK(m.mode):
+        if xstat.lutime or not stat.S_ISLNK(m.mode):
             print 'atime: ' + fstimestr(m.atime)
         else:
             print 'atime: 0'
     if 'mtime' in active_fields:
-        if xstat._have_utimensat or not stat.S_ISLNK(m.mode):
+        if xstat.lutime or not stat.S_ISLNK(m.mode):
             print 'mtime: ' + fstimestr(m.mtime)
         else:
             print 'mtime: 0'
index 3b9fcc130b33d5cf1b8375e937d578d5307b1e82..1aaf8ccf8749caffee4bb1289469645acbc6d2ea 100644 (file)
@@ -697,81 +697,109 @@ static PyObject *bup_set_linux_file_attr(PyObject *self, PyObject *args)
 
 
 #ifdef HAVE_UTIMENSAT
-#if defined(_ATFILE_SOURCE) \
-  || _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L
-#define HAVE_BUP_UTIMENSAT 1
 
-static PyObject *bup_utimensat(PyObject *self, PyObject *args)
+static int bup_parse_xutime_args(char **path,
+                                 long *access,
+                                 long *access_ns,
+                                 long *modification,
+                                 long *modification_ns,
+                                 PyObject *self, PyObject *args)
 {
-    int rc, dirfd, flags;
-    char *path;
-    long access, access_ns, modification, modification_ns;
-    struct timespec ts[2];
-
-    if (!PyArg_ParseTuple(args, "is((ll)(ll))i",
-                          &dirfd,
-                          &path,
-                          &access, &access_ns,
-                          &modification, &modification_ns,
-                          &flags))
-        return NULL;
+    if (!PyArg_ParseTuple(args, "s((ll)(ll))",
+                          path,
+                          access, access_ns,
+                          modification, modification_ns))
+        return 0;
 
-    if (isnan(access))
+    if (isnan(*access))
     {
         PyErr_SetString(PyExc_ValueError, "access time is NaN");
-        return NULL;
+        return 0;
     }
-    else if (isinf(access))
+    else if (isinf(*access))
     {
         PyErr_SetString(PyExc_ValueError, "access time is infinite");
-        return NULL;
+        return 0;
     }
-    else if (isnan(modification))
+    else if (isnan(*modification))
     {
         PyErr_SetString(PyExc_ValueError, "modification time is NaN");
-        return NULL;
+        return 0;
     }
-    else if (isinf(modification))
+    else if (isinf(*modification))
     {
         PyErr_SetString(PyExc_ValueError, "modification time is infinite");
-        return NULL;
+        return 0;
     }
 
-    if (isnan(access_ns))
+    if (isnan(*access_ns))
     {
         PyErr_SetString(PyExc_ValueError, "access time ns is NaN");
-        return NULL;
+        return 0;
     }
-    else if (isinf(access_ns))
+    else if (isinf(*access_ns))
     {
         PyErr_SetString(PyExc_ValueError, "access time ns is infinite");
-        return NULL;
+        return 0;
     }
-    else if (isnan(modification_ns))
+    else if (isnan(*modification_ns))
     {
         PyErr_SetString(PyExc_ValueError, "modification time ns is NaN");
-        return NULL;
+        return 0;
     }
-    else if (isinf(modification_ns))
+    else if (isinf(*modification_ns))
     {
         PyErr_SetString(PyExc_ValueError, "modification time ns is infinite");
-        return NULL;
+        return 0;
     }
 
+    return 1;
+}
+
+#endif /* def HAVE_UTIMENSAT */
+
+
+#ifdef HAVE_UTIMENSAT
+
+static PyObject *bup_xutime_ns(PyObject *self, PyObject *args,
+                               int follow_symlinks)
+{
+    int rc;
+    char *path;
+    long access, access_ns, modification, modification_ns;
+    struct timespec ts[2];
+
+    if (!bup_parse_xutime_args(&path, &access, &access_ns,
+                               &modification, &modification_ns,
+                               self, args))
+       return NULL;
+
     ts[0].tv_sec = access;
     ts[0].tv_nsec = access_ns;
     ts[1].tv_sec = modification;
     ts[1].tv_nsec = modification_ns;
-
-    rc = utimensat(dirfd, path, ts, flags);
+    rc = utimensat(AT_FDCWD, path, ts,
+                   follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW);
     if (rc != 0)
         return PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
 
     return Py_BuildValue("O", Py_None);
 }
 
-#endif /* defined(_ATFILE_SOURCE)
-          || _XOPEN_SOURCE >= 700 || _POSIX_C_SOURCE >= 200809L */
+
+#define BUP_HAVE_BUP_UTIME_NS 1
+static PyObject *bup_utime_ns(PyObject *self, PyObject *args)
+{
+    return bup_xutime_ns(self, args, 1);
+}
+
+
+#define BUP_HAVE_BUP_LUTIME_NS 1
+static PyObject *bup_lutime_ns(PyObject *self, PyObject *args)
+{
+    return bup_xutime_ns(self, args, 0);
+}
+
 #endif /* HAVE_UTIMENSAT */
 
 
@@ -915,9 +943,14 @@ static PyMethodDef helper_methods[] = {
     { "set_linux_file_attr", bup_set_linux_file_attr, METH_VARARGS,
       "Set the Linux attributes for the given file." },
 #endif
-#ifdef HAVE_BUP_UTIMENSAT
-    { "utimensat", bup_utimensat, METH_VARARGS,
-      "Change file timestamps with nanosecond precision." },
+#ifdef BUP_HAVE_BUP_UTIME_NS
+    { "bup_utime_ns", bup_utime_ns, METH_VARARGS,
+      "Change path timestamps with up to nanosecond precision." },
+#endif
+#ifdef BUP_HAVE_BUP_LUTIME_NS
+    { "bup_lutime_ns", bup_lutime_ns, METH_VARARGS,
+      "Change path timestamps with up to nanosecond precision;"
+      " don't follow symlinks." },
 #endif
     { "stat", bup_stat, METH_VARARGS,
       "Extended version of stat." },
@@ -935,11 +968,6 @@ PyMODINIT_FUNC init_helpers(void)
     PyObject *m = Py_InitModule("_helpers", helper_methods);
     if (m == NULL)
         return;
-#ifdef HAVE_BUP_UTIMENSAT
-    PyModule_AddObject(m, "AT_FDCWD", Py_BuildValue("i", AT_FDCWD));
-    PyModule_AddObject(m, "AT_SYMLINK_NOFOLLOW",
-                       Py_BuildValue("i", AT_SYMLINK_NOFOLLOW));
-#endif
     e = getenv("BUP_FORCE_TTY");
     istty2 = isatty(2) || (atoi(e ? e : "0") & 2);
     unpythonize_argv();
index b3884a4ac88753f490287e8ef8375d1e1b181ece..61df9fbd92e108a387ee52b1c4c6718ad03d674d 100644 (file)
@@ -305,7 +305,7 @@ class Metadata:
     def _apply_common_rec(self, path, restore_numeric_ids=False):
         # FIXME: S_ISDOOR, S_IFMPB, S_IFCMP, S_IFNWK, ... see stat(2).
         # EACCES errors at this stage are fatal for the current path.
-        if stat.S_ISLNK(self.mode):
+        if lutime and stat.S_ISLNK(self.mode):
             try:
                 lutime(path, (self.atime, self.mtime))
             except OSError, e:
index 7ebeeeb1519f8bdb4697b86334c0d5abbbb2ade5..e378c75b6180986bc251a33d5ff3ffefe07c276f 100644 (file)
@@ -39,14 +39,21 @@ def test_fstime():
     WVPASSEQ(type(xstat.fstime_floor_secs(-10**9 / 2)), type(0))
 
 
+try:
+    _have_bup_utime_ns = _helpers.bup_utime_ns
+except AttributeError, e:
+    _have_bup_utime_ns = False
+
 @wvtest
 def test_timespec_behavior():
+    if not _have_bup_utime_ns:
+        return
     tmpdir = tempfile.mkdtemp(prefix='bup-tmetadata-')
     try:
         path = tmpdir + '/foo'
         open(path, 'w').close()
         frac_ts = (0, 10**9 / 2)
-        _helpers.utimensat(_helpers.AT_FDCWD, path, (frac_ts, frac_ts), 0)
+        _helpers.bup_utime_ns(path, (frac_ts, frac_ts))
         st = _helpers.stat(path)
         atime_ts = st[8]
         mtime_ts = st[9]
@@ -57,7 +64,7 @@ def test_timespec_behavior():
         if(mtime_ts[1] == frac_ts[1]):
             # Sub-second resolution -- check behavior of negative timespecs.
             neg_ts = (-43, 10**9 / 2)
-            _helpers.utimensat(_helpers.AT_FDCWD, path, (neg_ts, neg_ts), 0)
+            _helpers.bup_utime_ns(path, (neg_ts, neg_ts))
             st = _helpers.stat(path)
             atime_ts = st[8]
             mtime_ts = st[9]
index c2533705ded008641213c4f407f659b164db8fb0..f3e11de341fcb4f874bc3d77ede25c73261edcbd 100644 (file)
@@ -4,9 +4,14 @@ from bup import _helpers
 
 
 try:
-    _have_utimensat = _helpers.utimensat
+    _have_bup_utime_ns = _helpers.bup_utime_ns
 except AttributeError, e:
-    _have_utimensat = False
+    _have_bup_utime_ns = False
+
+try:
+    _have_bup_lutime_ns = _helpers.bup_lutime_ns
+except AttributeError, e:
+    _have_bup_lutime_ns = False
 
 
 def timespec_to_nsecs((ts_s, ts_ns)):
@@ -32,26 +37,30 @@ def fstime_to_timespec(ns):
     return nsecs_to_timespec(ns)
 
 
-if _have_utimensat:
-    def lutime(path, times):
-        atime = nsecs_to_timespec(times[0])
-        mtime = nsecs_to_timespec(times[1])
-        _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime),
-                           _helpers.AT_SYMLINK_NOFOLLOW)
+if _have_bup_utime_ns:
     def utime(path, times):
+        """Times must be provided as (atime_ns, mtime_ns)."""
         atime = nsecs_to_timespec(times[0])
         mtime = nsecs_to_timespec(times[1])
-        _helpers.utimensat(_helpers.AT_FDCWD, path, (atime, mtime), 0)
+        _helpers.bup_utime_ns(path, (atime, mtime))
 else:
-    def lutime(path, times):
-        return None
-
     def utime(path, times):
+        """Times must be provided as (atime_ns, mtime_ns)."""
         atime = fstime_floor_secs(times[0])
         mtime = fstime_floor_secs(times[1])
         os.utime(path, (atime, mtime))
 
 
+if _have_bup_lutime_ns:
+    def lutime(path, times):
+        """Times must be provided as (atime_ns, mtime_ns)."""
+        atime = nsecs_to_timespec(times[0])
+        mtime = nsecs_to_timespec(times[1])
+        _helpers.bup_lutime_ns(path, (atime, mtime))
+else:
+    lutime = False
+
+
 class stat_result:
     @staticmethod
     def from_xstat_rep(st):