Error (and fix) restoring to a non-local directory

Ben Escoto bescoto@stanford.edu
Sun, 18 Aug 2002 10:57:34 -0700


--==_Exmh_968037334P
Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
Content-Id: <2153.1029693431.0@folly.Stanford.EDU>

------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <2153.1029693431.1@folly.Stanford.EDU>

>>>>> "PJE" == Phillip J Eby <pje@telecommunity.com>
>>>>> wrote the following on Sun, 18 Aug 2002 11:12:17 -0400

  PJE> I got the following error trying to do a restore from a local
  PJE> drive to a drive over an SSH connection, using 0.9.5:
    ...
  PJE> To fix it, I looked at the code and fixed up the "else" branch
  PJE> that was being executed for a restore from a local to a
  PJE> non-local drive, so that it set 'rdir=getpath(cp2)', since that
  PJE> seemed consistent with branches for other operations.  Restore
  PJE> then seemed to work okay.

Are you sure this works?  The fix seems more complicated than that to
me (see bottom).

  PJE> Apparently, it's not possible to restore a new backup that has
  PJE> no increments yet.  I didn't try to trace this one down,
  PJE> however, since my simple workaround at this point is to always
  PJE> run rdiff-backup *twice* when I first create a backup.  ;)

Thanks for reporting these two bugs.  I'm almost relieved since I've
gotten a variety of backup bug reports but very few restore
reports...  Makes me wonder if no one is restoring anything...

    Anyway, here are the changes which make rdiff-backup pass my
(newly added) tests.


-- 
Ben Escoto


------- =_aaaaaaaaaa0
Content-Type: text/patches; charset="us-ascii"
Content-ID: <2153.1029693431.2@folly.Stanford.EDU>

diff -u -r1.3 -r1.4
--- Security.py 25 Jul 2002 06:34:48 -0000      1.3
+++ Security.py 18 Aug 2002 17:43:20 -0000      1.4
@@ -10,8 +10,8 @@
 
 """Functions to make sure remote requests are kosher"""
 
-import sys
-import Globals, tempfile
+import sys, tempfile
+import Globals, Main
 from rpath import *
 
 class Violation(Exception):
@@ -67,6 +67,8 @@
                        rdir = tempfile.gettempdir()
                elif islocal(cp1):
                        sec_level = "read-only"
+                       rdir = Main.restore_get_root(RPath(Globals.local_connection,
+                                                                          getpath(cp1)))[0].path
                else:
                        assert islocal(cp2)
                        sec_level = "all"
@@ -89,7 +91,8 @@
        else: assert 0, "Unknown action %s" % action
 
        Globals.security_level = sec_level
-       Globals.restrict_path = rdir
+       Globals.restrict_path = RPath(Globals.local_connection,
+                                                                 rdir).normalize().path
 
 def set_allowed_requests(sec_level):
        """Set the allowed requests list using the security level"""
@@ -111,7 +114,9 @@
                                                                 "Time.setcurtime_local",
                                                                 "Resume.ResumeCheck",
                                                                 "HLSourceStruct.split_initial_dsiter",
-                                                                "HLSourceStruct.get_diffs_and_finalize"])
+                                                                "HLSourceStruct.get_diffs_and_finalize",
+                                                                "RPathStatic.gzip_open_local_read",
+                                                                "RPathStatic.open_local_read"])
                if sec_level == "update-only":
                        allowed_requests. \
                                extend(["Log.open_logfile_local", "Log.close_logfile_local",


diff -u -r1.12 -r1.13
--- restore.py  25 Jun 2002 18:04:08 -0000      1.12
+++ restore.py  18 Aug 2002 17:43:20 -0000      1.13
@@ -79,6 +79,7 @@
                """Returns increments with given base"""
                dirname, basename = inc_rpath.dirsplit()
                parent_dir = RPath(inc_rpath.conn, dirname, ())
+               if not parent_dir.isdir(): return [] # inc directory not created yet
                index = inc_rpath.index
 
                if index:


diff -u -r1.21 -r1.22
--- rpath.py    10 Aug 2002 00:43:04 -0000      1.21
+++ rpath.py    18 Aug 2002 17:43:20 -0000      1.22
@@ -200,10 +200,26 @@
                try: return tuple(os.lstat(filename))
                except os.error: return None
 
-       def make_socket(path):
-               """Make a local socket at the given path"""
+       def make_socket_local(rpath):
+               """Make a local socket at the given path
+
+               This takes an rpath so that it will be checked by Security.
+               (Miscellaneous strings will not be.)
+
+               """
+               assert rpath.conn is Globals.local_connection
                s = socket.socket(socket.AF_UNIX)
-               s.bind(path)
+               s.bind(rpath.path)
+
+       def gzip_open_local_read(rpath):
+               """Return open GzipFile.  See security note directly above"""
+               assert rpath.conn is Globals.local_connection
+               return gzip.GzipFile(rpath.path, "rb")
+
+       def open_local_read(rpath):
+               """Return open file (provided for security reasons)"""
+               assert rpath.conn is Globals.local_connection
+               return open(rpath.path, "rb")
 
 MakeStatic(RPathStatic)
 
@@ -587,7 +603,7 @@
 
        def mksock(self):
                """Make a socket at self.path"""
-               self.conn.RPathStatic.make_socket(self.path)
+               self.conn.RPathStatic.make_socket_local(self)
                self.setdata()
                assert self.issock()
 
@@ -700,11 +716,23 @@
                """Return open file.  Supports modes "w" and "r".
 
                If compress is true, data written/read will be gzip
-               compressed/decompressed on the fly.
-
-               """
-               if compress: return self.conn.gzip.GzipFile(self.path, mode)
-               else: return self.conn.open(self.path, mode)
+               compressed/decompressed on the fly.  The extra complications
+               below are for security reasons - try to make the extent of the
+               risk apparent from the remote call.
+
+               """
+               if self.conn is Globals.local_connection:
+                       if compress: return gzip.GzipFile(self.path, mode)
+                       else: return open(self.path, mode)
+
+               if compress:
+                       if mode == "r" or mode == "rb":
+                               return self.conn.RPathStatic.gzip_open_local_read(self)
+                       else: return self.conn.gzip.GzipFile(self.path, mode)
+               else:
+                       if mode == "r" or mode == "rb":
+                               return self.conn.RPathStatic.open_local_read(self)
+                       else: return self.conn.open(self.path, mode)
 
        def write_from_fileobj(self, fp, compress = None):
                """Reads fp and writes to self.path.  Closes both when done


------- =_aaaaaaaaaa0--

--==_Exmh_968037334P
Content-Type: application/pgp-signature

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)
Comment: Exmh version 2.5 01/15/2001

iD8DBQE9X+AK+owuOvknOnURAi23AKCANlcvGvVr+E79JvYOIjvTFLO+dACdG5Pl
obeFoc0LUoMuNqaTMXJ09Hk=
=FbPi
-----END PGP SIGNATURE-----

--==_Exmh_968037334P--