reject transfers when there isn't enough disk space available
closes #91 Also tweaks an error message: don't say "refusing to clobber pre-existing file FOO" when we don't check that it's actually a file. Just say ".. pre-existing 'FOO'".
This commit is contained in:
		
							parent
							
								
									b57928431a
								
							
						
					
					
						commit
						f3e1aab3a1
					
				| 
						 | 
					@ -8,7 +8,8 @@ from twisted.python import log
 | 
				
			||||||
from ..wormhole import wormhole
 | 
					from ..wormhole import wormhole
 | 
				
			||||||
from ..transit import TransitReceiver
 | 
					from ..transit import TransitReceiver
 | 
				
			||||||
from ..errors import TransferError, WormholeClosedError
 | 
					from ..errors import TransferError, WormholeClosedError
 | 
				
			||||||
from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr
 | 
					from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr,
 | 
				
			||||||
 | 
					                    estimate_free_space)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
APPID = u"lothar.com/wormhole/text-or-file-xfer"
 | 
					APPID = u"lothar.com/wormhole/text-or-file-xfer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,6 +199,11 @@ class TwistedReceiver:
 | 
				
			||||||
        self.abs_destname = self._decide_destname("file",
 | 
					        self.abs_destname = self._decide_destname("file",
 | 
				
			||||||
                                                  file_data["filename"])
 | 
					                                                  file_data["filename"])
 | 
				
			||||||
        self.xfersize = file_data["filesize"]
 | 
					        self.xfersize = file_data["filesize"]
 | 
				
			||||||
 | 
					        free = estimate_free_space(self.abs_destname)
 | 
				
			||||||
 | 
					        if free is not None and free < self.xfersize:
 | 
				
			||||||
 | 
					            self._msg(u"Error: insufficient free space (%sB) for file (%sB)"
 | 
				
			||||||
 | 
					                      % (free, self.xfersize))
 | 
				
			||||||
 | 
					            raise TransferRejectedError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._msg(u"Receiving file (%s) into: %s" %
 | 
					        self._msg(u"Receiving file (%s) into: %s" %
 | 
				
			||||||
                  (naturalsize(self.xfersize), os.path.basename(self.abs_destname)))
 | 
					                  (naturalsize(self.xfersize), os.path.basename(self.abs_destname)))
 | 
				
			||||||
| 
						 | 
					@ -214,6 +220,11 @@ class TwistedReceiver:
 | 
				
			||||||
        self.abs_destname = self._decide_destname("directory",
 | 
					        self.abs_destname = self._decide_destname("directory",
 | 
				
			||||||
                                                  file_data["dirname"])
 | 
					                                                  file_data["dirname"])
 | 
				
			||||||
        self.xfersize = file_data["zipsize"]
 | 
					        self.xfersize = file_data["zipsize"]
 | 
				
			||||||
 | 
					        free = estimate_free_space(self.abs_destname)
 | 
				
			||||||
 | 
					        if free is not None and free < file_data["numbytes"]:
 | 
				
			||||||
 | 
					            self._msg(u"Error: insufficient free space (%sB) for directory (%sB)"
 | 
				
			||||||
 | 
					                      % (free, file_data["numbytes"]))
 | 
				
			||||||
 | 
					            raise TransferRejectedError()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._msg(u"Receiving directory (%s) into: %s/" %
 | 
					        self._msg(u"Receiving directory (%s) into: %s/" %
 | 
				
			||||||
                  (naturalsize(self.xfersize), os.path.basename(self.abs_destname)))
 | 
					                  (naturalsize(self.xfersize), os.path.basename(self.abs_destname)))
 | 
				
			||||||
| 
						 | 
					@ -232,8 +243,7 @@ class TwistedReceiver:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # get confirmation from the user before writing to the local directory
 | 
					        # get confirmation from the user before writing to the local directory
 | 
				
			||||||
        if os.path.exists(abs_destname):
 | 
					        if os.path.exists(abs_destname):
 | 
				
			||||||
            self._msg(u"Error: refusing to overwrite existing %s %s" %
 | 
					            self._msg(u"Error: refusing to overwrite existing '%s'" % destname)
 | 
				
			||||||
                      (mode, destname))
 | 
					 | 
				
			||||||
            raise TransferRejectedError()
 | 
					            raise TransferRejectedError()
 | 
				
			||||||
        return abs_destname
 | 
					        return abs_destname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -449,7 +449,9 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
 | 
				
			||||||
        return self._do_test(mode="directory", override_filename=True)
 | 
					        return self._do_test(mode="directory", override_filename=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @inlineCallbacks
 | 
					    @inlineCallbacks
 | 
				
			||||||
    def test_file_noclobber(self):
 | 
					    def _do_test_fail(self, mode, failmode):
 | 
				
			||||||
 | 
					        assert mode in ("file", "directory")
 | 
				
			||||||
 | 
					        assert failmode in ("noclobber", "toobig")
 | 
				
			||||||
        send_cfg = config("send")
 | 
					        send_cfg = config("send")
 | 
				
			||||||
        recv_cfg = config("receive")
 | 
					        recv_cfg = config("receive")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -458,29 +460,42 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
 | 
				
			||||||
            cfg.relay_url = self.relayurl
 | 
					            cfg.relay_url = self.relayurl
 | 
				
			||||||
            cfg.transit_helper = ""
 | 
					            cfg.transit_helper = ""
 | 
				
			||||||
            cfg.listen = False
 | 
					            cfg.listen = False
 | 
				
			||||||
            cfg.code = code = "1-abc"
 | 
					            cfg.code = "1-abc"
 | 
				
			||||||
            cfg.stdout = io.StringIO()
 | 
					            cfg.stdout = io.StringIO()
 | 
				
			||||||
            cfg.stderr = io.StringIO()
 | 
					            cfg.stderr = io.StringIO()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        message = "test message"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        recv_cfg.accept_file = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        send_dir = self.mktemp()
 | 
					        send_dir = self.mktemp()
 | 
				
			||||||
        os.mkdir(send_dir)
 | 
					        os.mkdir(send_dir)
 | 
				
			||||||
        receive_dir = self.mktemp()
 | 
					        receive_dir = self.mktemp()
 | 
				
			||||||
        os.mkdir(receive_dir)
 | 
					        os.mkdir(receive_dir)
 | 
				
			||||||
 | 
					        recv_cfg.accept_file = True # don't ask for permission
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        send_filename = "testfile"
 | 
					        if mode == "file":
 | 
				
			||||||
        with open(os.path.join(send_dir, send_filename), "w") as f:
 | 
					            message = "test message\n"
 | 
				
			||||||
            f.write(message)
 | 
					            send_cfg.what = receive_name = send_filename = "testfile"
 | 
				
			||||||
        send_cfg.what = receive_filename = send_filename
 | 
					            with open(os.path.join(send_dir, send_filename), "w") as f:
 | 
				
			||||||
        recv_cfg.what = receive_filename
 | 
					                f.write(message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        PRESERVE = "don't clobber me\n"
 | 
					        elif mode == "directory":
 | 
				
			||||||
        clobberable = os.path.join(receive_dir, receive_filename)
 | 
					            # $send_dir/
 | 
				
			||||||
        with open(clobberable, "w") as f:
 | 
					            # $send_dir/$dirname/
 | 
				
			||||||
            f.write(PRESERVE)
 | 
					            # $send_dir/$dirname/[12345]
 | 
				
			||||||
 | 
					            # cd $send_dir && wormhole send $dirname
 | 
				
			||||||
 | 
					            # cd $receive_dir && wormhole receive
 | 
				
			||||||
 | 
					            # expect: $receive_dir/$dirname/[12345]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            send_cfg.what = receive_name = send_dirname = "testdir"
 | 
				
			||||||
 | 
					            os.mkdir(os.path.join(send_dir, send_dirname))
 | 
				
			||||||
 | 
					            for i in range(5):
 | 
				
			||||||
 | 
					                path = os.path.join(send_dir, send_dirname, str(i))
 | 
				
			||||||
 | 
					                with open(path, "w") as f:
 | 
				
			||||||
 | 
					                    f.write("test message %d\n" % i)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if failmode == "noclobber":
 | 
				
			||||||
 | 
					            PRESERVE = "don't clobber me\n"
 | 
				
			||||||
 | 
					            clobberable = os.path.join(receive_dir, receive_name)
 | 
				
			||||||
 | 
					            with open(clobberable, "w") as f:
 | 
				
			||||||
 | 
					                f.write(PRESERVE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        send_cfg.cwd = send_dir
 | 
					        send_cfg.cwd = send_dir
 | 
				
			||||||
        send_d = cmd_send.send(send_cfg)
 | 
					        send_d = cmd_send.send(send_cfg)
 | 
				
			||||||
| 
						 | 
					@ -488,13 +503,17 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
 | 
				
			||||||
        recv_cfg.cwd = receive_dir
 | 
					        recv_cfg.cwd = receive_dir
 | 
				
			||||||
        receive_d = cmd_receive.receive(recv_cfg)
 | 
					        receive_d = cmd_receive.receive(recv_cfg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # both sides will fail because of the pre-existing file
 | 
					        # both sides will fail
 | 
				
			||||||
 | 
					        if failmode == "noclobber":
 | 
				
			||||||
        f = yield self.assertFailure(send_d, TransferError)
 | 
					            free_space = 10000000
 | 
				
			||||||
        self.assertEqual(str(f), "remote error, transfer abandoned: transfer rejected")
 | 
					        else:
 | 
				
			||||||
 | 
					            free_space = 0
 | 
				
			||||||
        f = yield self.assertFailure(receive_d, TransferError)
 | 
					        with mock.patch("wormhole.cli.cmd_receive.estimate_free_space",
 | 
				
			||||||
        self.assertEqual(str(f), "transfer rejected")
 | 
					                        return_value=free_space):
 | 
				
			||||||
 | 
					            f = yield self.assertFailure(send_d, TransferError)
 | 
				
			||||||
 | 
					            self.assertEqual(str(f), "remote error, transfer abandoned: transfer rejected")
 | 
				
			||||||
 | 
					            f = yield self.assertFailure(receive_d, TransferError)
 | 
				
			||||||
 | 
					            self.assertEqual(str(f), "transfer rejected")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        send_stdout = send_cfg.stdout.getvalue()
 | 
					        send_stdout = send_cfg.stdout.getvalue()
 | 
				
			||||||
        send_stderr = send_cfg.stderr.getvalue()
 | 
					        send_stderr = send_cfg.stderr.getvalue()
 | 
				
			||||||
| 
						 | 
					@ -513,28 +532,74 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
 | 
				
			||||||
                             (receive_stdout, receive_stderr))
 | 
					                             (receive_stdout, receive_stderr))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # check sender
 | 
					        # check sender
 | 
				
			||||||
        self.failUnlessIn("Sending {size:s} file named '{name}'{NL}"
 | 
					        if mode == "file":
 | 
				
			||||||
                          .format(size=naturalsize(len(message)),
 | 
					            self.failUnlessIn("Sending {size:s} file named '{name}'{NL}"
 | 
				
			||||||
                                  name=send_filename,
 | 
					                              .format(size=naturalsize(len(message)),
 | 
				
			||||||
                                  NL=NL), send_stdout)
 | 
					                                      name=send_filename,
 | 
				
			||||||
        self.failUnlessIn("On the other computer, please run: "
 | 
					                                      NL=NL), send_stdout)
 | 
				
			||||||
                          "wormhole receive{NL}"
 | 
					            self.failUnlessIn("On the other computer, please run: "
 | 
				
			||||||
                          "Wormhole code is: {code}{NL}{NL}"
 | 
					                              "wormhole receive{NL}"
 | 
				
			||||||
                          .format(code=code, NL=NL),
 | 
					                              "Wormhole code is: {code}{NL}{NL}"
 | 
				
			||||||
                          send_stdout)
 | 
					                              .format(code=send_cfg.code, NL=NL),
 | 
				
			||||||
        self.failIfIn("File sent.. waiting for confirmation{NL}"
 | 
					                              send_stdout)
 | 
				
			||||||
                      "Confirmation received. Transfer complete.{NL}"
 | 
					            self.failIfIn("File sent.. waiting for confirmation{NL}"
 | 
				
			||||||
                      .format(NL=NL), send_stdout)
 | 
					                          "Confirmation received. Transfer complete.{NL}"
 | 
				
			||||||
 | 
					                          .format(NL=NL), send_stdout)
 | 
				
			||||||
 | 
					        elif mode == "directory":
 | 
				
			||||||
 | 
					            self.failUnlessIn("Sending directory", send_stdout)
 | 
				
			||||||
 | 
					            self.failUnlessIn("named 'testdir'", send_stdout)
 | 
				
			||||||
 | 
					            self.failUnlessIn("On the other computer, please run: "
 | 
				
			||||||
 | 
					                              "wormhole receive{NL}"
 | 
				
			||||||
 | 
					                              "Wormhole code is: {code}{NL}{NL}"
 | 
				
			||||||
 | 
					                              .format(code=send_cfg.code, NL=NL), send_stdout)
 | 
				
			||||||
 | 
					            self.failIfIn("File sent.. waiting for confirmation{NL}"
 | 
				
			||||||
 | 
					                          "Confirmation received. Transfer complete.{NL}"
 | 
				
			||||||
 | 
					                          .format(NL=NL), send_stdout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # check receiver
 | 
					        # check receiver
 | 
				
			||||||
        self.failUnlessIn("Error: "
 | 
					        if mode == "file":
 | 
				
			||||||
                          "refusing to overwrite existing file testfile{NL}"
 | 
					            self.failIfIn("Received file written to ", receive_stdout)
 | 
				
			||||||
                          .format(NL=NL), receive_stdout)
 | 
					            if failmode == "noclobber":
 | 
				
			||||||
        self.failIfIn("Received file written to ", receive_stdout)
 | 
					                self.failUnlessIn("Error: "
 | 
				
			||||||
        fn = os.path.join(receive_dir, receive_filename)
 | 
					                                  "refusing to overwrite existing 'testfile'{NL}"
 | 
				
			||||||
        self.failUnless(os.path.exists(fn))
 | 
					                                  .format(NL=NL), receive_stdout)
 | 
				
			||||||
        with open(fn, "r") as f:
 | 
					            else:
 | 
				
			||||||
            self.failUnlessEqual(f.read(), PRESERVE)
 | 
					                self.failUnlessIn("Error: "
 | 
				
			||||||
 | 
					                                  "insufficient free space (0B) for file (13B){NL}"
 | 
				
			||||||
 | 
					                                  .format(NL=NL), receive_stdout)
 | 
				
			||||||
 | 
					        elif mode == "directory":
 | 
				
			||||||
 | 
					            self.failIfIn("Received files written to {name}"
 | 
				
			||||||
 | 
					                          .format(name=receive_name), receive_stdout)
 | 
				
			||||||
 | 
					            #want = (r"Receiving directory \(\d+ \w+\) into: {name}/"
 | 
				
			||||||
 | 
					            #        .format(name=receive_name))
 | 
				
			||||||
 | 
					            #self.failUnless(re.search(want, receive_stdout),
 | 
				
			||||||
 | 
					            #                (want, receive_stdout))
 | 
				
			||||||
 | 
					            if failmode == "noclobber":
 | 
				
			||||||
 | 
					                self.failUnlessIn("Error: "
 | 
				
			||||||
 | 
					                                  "refusing to overwrite existing 'testdir'{NL}"
 | 
				
			||||||
 | 
					                                  .format(NL=NL), receive_stdout)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.failUnlessIn("Error: "
 | 
				
			||||||
 | 
					                                  "insufficient free space (0B) for directory (75B){NL}"
 | 
				
			||||||
 | 
					                                  .format(NL=NL), receive_stdout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if failmode == "noclobber":
 | 
				
			||||||
 | 
					            fn = os.path.join(receive_dir, receive_name)
 | 
				
			||||||
 | 
					            self.failUnless(os.path.exists(fn))
 | 
				
			||||||
 | 
					            with open(fn, "r") as f:
 | 
				
			||||||
 | 
					                self.failUnlessEqual(f.read(), PRESERVE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # check server stats
 | 
				
			||||||
 | 
					        self._rendezvous.get_stats()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_fail_file_noclobber(self):
 | 
				
			||||||
 | 
					        return self._do_test_fail("file", "noclobber")
 | 
				
			||||||
 | 
					    def test_fail_directory_noclobber(self):
 | 
				
			||||||
 | 
					        return self._do_test_fail("directory", "noclobber")
 | 
				
			||||||
 | 
					    def test_fail_file_toobig(self):
 | 
				
			||||||
 | 
					        return self._do_test_fail("file", "toobig")
 | 
				
			||||||
 | 
					    def test_fail_directory_toobig(self):
 | 
				
			||||||
 | 
					        return self._do_test_fail("directory", "toobig")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NotWelcome(ServerBase, unittest.TestCase):
 | 
					class NotWelcome(ServerBase, unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user