]> arthur.barton.de Git - bup.git/blobdiff - lib/bup/metadata.py
Add a helpers.FSTime class to handle filesystem timestamps and use it.
[bup.git] / lib / bup / metadata.py
index 6cd744805e50c9e8ee98ec5b2eb5209cb4373030..d520f5129a7fb157aace901ce77f1d99b6f6f15a 100644 (file)
@@ -9,8 +9,11 @@ import errno, os, sys, stat, pwd, grp, struct, xattr, posix1e, re
 
 from cStringIO import StringIO
 from bup import vint
-from bup.helpers import add_error, mkdirp, log, utime, lutime, lstat
-from bup._helpers import get_linux_file_attr, set_linux_file_attr
+from bup.helpers import add_error, mkdirp, log, utime, lutime, lstat, FSTime
+import bup._helpers as _helpers
+
+if _helpers.get_linux_file_attr:
+    from bup._helpers import get_linux_file_attr, set_linux_file_attr
 
 # WARNING: the metadata encoding is *not* stable yet.  Caveat emptor!
 
@@ -129,18 +132,6 @@ 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
@@ -185,9 +176,9 @@ class Metadata:
         self.group = grp.getgrgid(st.st_gid)[0]
 
     def _encode_common(self):
-        atime = _normalize_ts(self.atime)
-        mtime = _normalize_ts(self.mtime)
-        ctime = _normalize_ts(self.ctime)
+        atime = self.atime.to_timespec()
+        mtime = self.mtime.to_timespec()
+        ctime = self.ctime.to_timespec()
         result = vint.pack('VVsVsVvVvVvV',
                            self.mode,
                            self.uid,
@@ -217,23 +208,31 @@ class Metadata:
          mtime_ns,
          self.ctime,
          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)
+        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))
 
     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)
+        except IOError, e:
+            if e.errno != errno.ENOENT:
+                raise
+        if st:
+            if stat.S_ISDIR(st.st_mode):
+                try:
+                    os.rmdir(path)
+                except OSError, e:
+                    if e.errno == errno.ENOTEMPTY:
+                        msg = 'refusing to overwrite non-empty dir' + path
+                        raise Exception(msg)
+                    raise
+            else:
+                os.unlink(path)
+
         if stat.S_ISREG(self.mode):
             os.mknod(path, 0600 | stat.S_IFREG)
         elif stat.S_ISDIR(self.mode):
@@ -268,11 +267,27 @@ class Metadata:
             elif not stat.S_ISLNK(self.mode):
                 os.chmod(path, 0)
 
+            # Don't try to restore owner unless we're root, and even
+            # if asked, don't try to restore the owner or group if
+            # it doesn't exist in the system db.
             uid = self.uid
             gid = self.gid
             if not restore_numeric_ids:
-                uid = pwd.getpwnam(self.owner)[2]
-                gid = grp.getgrnam(self.group)[2]
+                if os.geteuid() == 0:
+                    try:
+                        uid = pwd.getpwnam(self.owner)[2]
+                    except KeyError:
+                        uid = -1
+                        log('bup: ignoring unknown owner %s for "%s"\n'
+                            % (self.owner, path))
+                else:
+                    uid = -1 # Not root; assume we can't change owner.
+                try:
+                    gid = grp.getgrnam(self.group)[2]
+                except KeyError:
+                    gid = -1
+                    log('bup: ignoring unknown group %s for "%s"\n'
+                        % (self.group, path))
             os.lchown(path, uid, gid)
 
             if _have_lchmod: