]> arthur.barton.de Git - bup.git/blobdiff - cmd/restore-cmd.py
Restore any metadata during "bup restore"; add "bup meta --edit".
[bup.git] / cmd / restore-cmd.py
index 6ebebec9fc405874c5f9d5440b438bf665df0f77..ff2d8b2fcf08a13233bfc5e78d0dcb2c419227b9 100755 (executable)
 #!/usr/bin/env python
-import sys, stat, time
-from bup import options, git, vfs
+import sys, stat
+from bup import options, git, metadata, vfs
 from bup.helpers import *
 
 optspec = """
 bup restore [-C outdir] </branch/revision/path/to/dir ...>
 --
-C,outdir=  change to given outdir before extracting files
-v,verbose  increase log output (can be used more than once)
-q,quiet    don't show progress meter
+C,outdir=   change to given outdir before extracting files
+numeric-ids restore numeric IDs (user, group, etc.) rather than names
+v,verbose   increase log output (can be used more than once)
+q,quiet     don't show progress meter
 """
 
-total_restored = last_progress = 0
+total_restored = 0
 
 
 def verbose1(s):
-    global last_progress
     if opt.verbose >= 1:
         print s
-        last_progress = 0
 
 
 def verbose2(s):
-    global last_progress
     if opt.verbose >= 2:
         print s
-        last_progress = 0
 
 
 def plog(s):
-    global last_progress
     if opt.quiet:
         return
-    now = time.time()
-    if now - last_progress > 0.2:
-        progress(s)
-        last_progress = now
+    qprogress(s)
 
 
-def do_node(top, n):
-    global total_restored
-    fullname = n.fullname(stop_at=top)
-    unlink(fullname)
+def print_info(n, fullname):
     if stat.S_ISDIR(n.mode):
         verbose1('%s/' % fullname)
-        mkdirp(fullname)
     elif stat.S_ISLNK(n.mode):
         verbose2('%s@ -> %s' % (fullname, n.readlink()))
-        os.symlink(n.readlink(), fullname)
     else:
         verbose2(fullname)
-        outf = open(fullname, 'wb')
-        try:
-            for b in chunkyreader(n.open()):
-                outf.write(b)
-        finally:
-            outf.close()
-    total_restored += 1
-    plog('Restoring: %d\r' % total_restored)
-    for sub in n:
-        do_node(top, sub)
-
-        
+
+
+def create_path(n, fullname, meta):
+    if meta:
+        meta.create_path(fullname)
+    else:
+        # These fallbacks are important -- meta could be null if, for
+        # example, save created a "fake" item, i.e. a new strip/graft
+        # path element, etc.  You can find cases like that by
+        # searching for "Metadata()".
+        unlink(fullname)
+        if stat.S_ISDIR(n.mode):
+            mkdirp(fullname)
+        elif stat.S_ISLNK(n.mode):
+            os.symlink(n.readlink(), fullname)
+
+
+def do_node(top, n, meta=None):
+    # meta will be None for dirs, and when there is no .bupm (i.e. no metadata)
+    global total_restored, opt
+    meta_stream = None
+    try:
+        fullname = n.fullname(stop_at=top)
+        # If this is a directory, its metadata is the first entry in
+        # any .bupm file inside the directory.  Get it.
+        if(stat.S_ISDIR(n.mode)):
+            mfile = n.metadata_file() # VFS file -- cannot close().
+            if mfile:
+                meta_stream = mfile.open()
+                meta = metadata.Metadata.read(meta_stream)
+        print_info(n, fullname)
+        create_path(n, fullname, meta)
+
+        # Write content if appropriate (only regular files have content).
+        plain_file = False
+        if meta:
+            plain_file = stat.S_ISREG(meta.mode)
+        else:
+            plain_file = stat.S_ISREG(n.mode)
+
+        if plain_file:
+            outf = open(fullname, 'wb')
+            try:
+                for b in chunkyreader(n.open()):
+                    outf.write(b)
+            finally:
+                outf.close()
+
+        total_restored += 1
+        plog('Restoring: %d\r' % total_restored)
+        for sub in n:
+            m = None
+            # Don't get metadata if this is a dir -- handled in sub do_node().
+            if meta_stream and not stat.S_ISDIR(sub.mode):
+                m = metadata.Metadata.read(meta_stream)
+            do_node(top, sub, m)
+        if meta:
+            meta.apply_to_path(fullname,
+                               restore_numeric_ids=opt.numeric_ids)
+    finally:
+        if meta_stream:
+            meta_stream.close()
+
 handle_ctrl_c()
 
 o = options.Options(optspec)
@@ -98,7 +137,7 @@ for d in extra:
         do_node(n.parent, n)
 
 if not opt.quiet:
-    log('Restoring: %d, done.\n' % total_restored)
+    progress('Restoring: %d, done.\n' % total_restored)
 
 if saved_errors:
     log('WARNING: %d errors encountered while restoring.\n' % len(saved_errors))