]> arthur.barton.de Git - bup.git/commitdiff
Add a metadata() method to Node, File, Symlink, and Dir VFS objects.
authorRob Browning <rlb@defaultvalue.org>
Sat, 21 Jul 2012 20:26:17 +0000 (15:26 -0500)
committerRob Browning <rlb@defaultvalue.org>
Sun, 30 Dec 2012 18:00:21 +0000 (12:00 -0600)
The current implementation just populates the self._metadata field of
all of the objects in a directory (and of directory itself) from the
relevant .bupm file whenever metadata() is called on any one of those
objects.

Signed-off-by: Rob Browning <rlb@defaultvalue.org>
Reviewed-by: Zoran Zaric <zz@zoranzaric.de>
Reviewed-by: Gabriel Filion <lelutin@gmail.com>
lib/bup/t/tmetadata.py
lib/bup/vfs.py

index ca2e55c53134b3099c4fce6897f7c2f08281ef2d..65c012a54d8a07c8a77035749d9cb5a0ab7cf729 100644 (file)
@@ -1,11 +1,14 @@
 import glob, grp, pwd, stat, tempfile, subprocess
 import bup.helpers as helpers
-from bup import metadata
+from bup import git, metadata, vfs
 from bup.helpers import clear_errors, detect_fakeroot, is_superuser
 from wvtest import *
+from bup.xstat import utime, lutime
 
 
-top_dir = os.getcwd()
+top_dir = '../../..'
+bup_path = top_dir + '/bup'
+start_dir = os.getcwd()
 
 
 def ex(*cmd):
@@ -108,6 +111,42 @@ def test_clean_up_extract_path():
     WVPASSEQ(cleanup('///foo/bar'), 'foo/bar')
 
 
+@wvtest
+def test_metadata_method():
+    tmpdir = tempfile.mkdtemp(prefix='bup-tmetadata-')
+    try:
+        bup_dir = tmpdir + '/bup'
+        data_path = tmpdir + '/foo'
+        os.mkdir(data_path)
+        ex('touch', data_path + '/file')
+        ex('ln', '-s', 'file', data_path + '/symlink')
+        test_time1 = 13 * 1000000000
+        test_time2 = 42 * 1000000000
+        utime(data_path + '/file', (0, test_time1))
+        lutime(data_path + '/symlink', (0, 0))
+        utime(data_path, (0, test_time2))
+        ex(bup_path, '-d', bup_dir, 'init')
+        ex(bup_path, '-d', bup_dir, 'index', '-v', data_path)
+        ex(bup_path, '-d', bup_dir, 'save', '-tvvn', 'test', data_path)
+        git.check_repo_or_die(bup_dir)
+        top = vfs.RefList(None)
+        n = top.lresolve('/test/latest' + data_path)
+        m = n.metadata()
+        WVPASS(m.mtime == test_time2)
+        WVPASS(len(n.subs()) == 2)
+        WVPASS(n.name == 'foo')
+        WVPASS(set([x.name for x in n.subs()]) == set(['file', 'symlink']))
+        for sub in n:
+            if sub.name == 'file':
+                m = sub.metadata()
+                WVPASS(m.mtime == test_time1)
+            elif sub.name == 'symlink':
+                m = sub.metadata()
+                WVPASS(m.mtime == 0)
+    finally:
+        subprocess.call(['rm', '-rf', tmpdir])
+
+
 def _first_err():
     if helpers.saved_errors:
         return str(helpers.saved_errors[0])
@@ -227,5 +266,5 @@ else:
         m.apply_to_path(path, restore_numeric_ids=False)
         WVPASSEQ(xattr.list(path), ['user.foo'])
         WVPASSEQ(xattr.get(path, 'user.foo'), 'bar')
-        os.chdir(top_dir)
+        os.chdir(start_dir)
         cleanup_testfs()
index f58dd5e31d3069dff375743376ffebf3c0671f55..59ec5f39c4dd66975e6e25f1e1f824220a3118fb 100644 (file)
@@ -4,7 +4,7 @@ The vfs.py library makes it possible to expose contents from bup's repository
 and abstracts internal name mangling and storage from the exposition layer.
 """
 import os, re, stat, time
-from bup import git
+from bup import git, metadata
 from helpers import *
 from bup.hashsplit import GIT_MODE_TREE, GIT_MODE_FILE
 
@@ -171,6 +171,7 @@ class Node:
         self.hash = hash
         self.ctime = self.mtime = self.atime = 0
         self._subs = None
+        self._metadata = None
 
     def __repr__(self):
         return "<bup.vfs.Node object at X - name:%r hash:%s parent:%r>" \
@@ -297,6 +298,16 @@ class Node:
         """Open the current node. It is an error to open a non-file node."""
         raise NotFile('%s is not a regular file' % self.name)
 
+    def _populate_metadata(self):
+        # Only Dirs contain .bupm files, so by default, do nothing.
+        pass
+
+    def metadata(self):
+        """Return this Node's Metadata() object, if any."""
+        if self.parent:
+            self.parent._populate_metadata()
+        return self._metadata
+
 
 class File(Node):
     """A normal file from bup's repository."""
@@ -386,7 +397,18 @@ class Dir(Node):
 
     def __init__(self, *args):
         Node.__init__(self, *args)
-        self._metadata = None
+        self._bupm = None
+
+    def _populate_metadata(self):
+        if not self._subs:
+            self._mksubs()
+        if not self._bupm:
+            return
+        meta_stream = self._bupm.open()
+        self._metadata = metadata.Metadata.read(meta_stream)
+        for sub in self:
+            if not stat.S_ISDIR(sub.mode):
+                sub._metadata = metadata.Metadata.read(meta_stream)
 
     def _mksubs(self):
         self._subs = {}
@@ -399,8 +421,7 @@ class Dir(Node):
         assert(type == 'tree')
         for (mode,mangled_name,sha) in git.tree_decode(''.join(it)):
             if mangled_name == '.bupm':
-                self._metadata = \
-                    File(self, mangled_name, mode, sha, git.BUP_NORMAL)
+                self._bupm = File(self, mangled_name, mode, sha, git.BUP_NORMAL)
                 continue
             name = mangled_name
             (name,bupmode) = git.demangle_name(mangled_name)
@@ -413,11 +434,16 @@ class Dir(Node):
             else:
                 self._subs[name] = File(self, name, mode, sha, bupmode)
 
-    def metadata_file(self):
-        if self._subs == None:
-            self._mksubs()
+    def metadata(self):
+        """Return this Dir's Metadata() object, if any."""
+        self._populate_metadata()
         return self._metadata
 
+    def metadata_file(self):
+        """Return this Dir's .bupm File, if any."""
+        self._populate_metadata()
+        return self._bupm
+
 
 class CommitDir(Node):
     """A directory that contains all commits that are reachable by a ref.