From 706494016c187f2ad36410653564a5702096b392 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 14 Oct 2021 18:29:02 +0530 Subject: [PATCH] More work on file transmission --- kitty/file_transmission.py | 18 +++++----- kitty_tests/file_transmission.py | 56 ++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/kitty/file_transmission.py b/kitty/file_transmission.py index 600666024..e28857ccd 100644 --- a/kitty/file_transmission.py +++ b/kitty/file_transmission.py @@ -70,13 +70,13 @@ def iter_file_metadata(file_specs: Iterable[Tuple[str, str]]) -> Iterator[Union[ def make_ftc(path: str, spec_id: str, sr: Optional[os.stat_result] = None) -> FileTransmissionCommand: if sr is None: - sr = os.stat(path) - if stat.S_ISREG(sr.st_mode): - ftype = FileType.regular - elif stat.S_ISLNK(sr.st_mode): + sr = os.stat(path, follow_symlinks=False) + if stat.S_ISLNK(sr.st_mode): ftype = FileType.symlink elif stat.S_ISDIR(sr.st_mode): ftype = FileType.directory + elif stat.S_ISREG(sr.st_mode): + ftype = FileType.regular else: raise ValueError('Not an appropriate file type') return FileTransmissionCommand( @@ -91,7 +91,7 @@ def iter_file_metadata(file_specs: Iterable[Tuple[str, str]]) -> Iterator[Union[ if not os.path.isabs(path): path = abspath(path, use_home=True) try: - sr = os.stat(path) + sr = os.stat(path, follow_symlinks=False) read_ok = os.access(path, os.R_OK) except OSError as err: errname = errno.errorcode.get(err.errno, 'EFAIL') @@ -129,7 +129,7 @@ def iter_file_metadata(file_specs: Iterable[Tuple[str, str]]) -> Iterator[Union[ pass else: try: - s = os.stat(dest) + s = os.stat(dest, follow_symlinks=False) except OSError: pass else: @@ -533,7 +533,7 @@ class ActiveSend: def __init__(self, request_id: str, quiet: int, bypass: str, num_of_args: int) -> None: self.id = request_id - self.args_remaining = num_of_args + self.expected_num_of_args = num_of_args self.bypass_ok: Optional[bool] = None self.accepted = False if bypass: @@ -547,7 +547,7 @@ class ActiveSend: @property def spec_complete(self) -> bool: - return self.args_remaining < 1 + return self.expected_num_of_args <= len(self.file_specs) def add_file_spec(self, cmd: FileTransmissionCommand) -> None: if len(self.file_specs) > 8192 or self.spec_complete: @@ -559,7 +559,7 @@ class ActiveSend: return monotonic() - self.last_activity_at > (60 * EXPIRE_TIME) def close(self) -> None: - raise NotImplementedError('TODO: Implement this') + pass # TODO: Implement this class FileTransmission: diff --git a/kitty_tests/file_transmission.py b/kitty_tests/file_transmission.py index e0b76242c..22a81b76a 100644 --- a/kitty_tests/file_transmission.py +++ b/kitty_tests/file_transmission.py @@ -86,9 +86,9 @@ class TestFileTransmission(BaseTest): b = tuple(f(r) for r in b if r.get('status') != 'PROGRESS') self.ae(a, b) - def assertResponses(self, ft, **kw): + def assertResponses(self, ft, limit=1024, **kw): self.responses.append(response(**kw)) - self.cr(ft.test_responses, self.responses) + self.cr(ft.test_responses[:limit], self.responses[:limit]) def assertPathEqual(self, a, b): a = os.path.abspath(os.path.realpath(a)) @@ -132,6 +132,58 @@ class TestFileTransmission(BaseTest): # test size of delta patch(a_path, a_path, c_path, max_delta_len=256) + def test_file_get(self): + # send refusal + for quiet in (0, 1, 2): + ft = FileTransmission(allow=False) + ft.handle_serialized_command(serialized_cmd(action='receive', id='x', quiet=quiet)) + self.cr(ft.test_responses, [] if quiet == 2 else [response(id='x', status='EPERM:User refused the transfer')]) + self.assertFalse(ft.active_sends) + # reading metadata for specs + cwd = os.path.join(self.tdir, 'cwd') + home = os.path.join(self.tdir, 'home') + os.mkdir(cwd), os.mkdir(home) + with set_paths(cwd=cwd, home=home): + ft = FileTransmission() + self.responses = [] + ft.handle_serialized_command(serialized_cmd(action='receive', size=1)) + self.assertResponses(ft, status='OK') + ft.handle_serialized_command(serialized_cmd(action='file', file_id='missing', data=b'XXX')) + self.responses.append(response(status='ENOENT:Failed to read spec', file_id='missing')) + self.assertResponses(ft, status='OK') + ft = FileTransmission() + self.responses = [] + ft.handle_serialized_command(serialized_cmd(action='receive', size=2)) + self.assertResponses(ft, status='OK') + with open(os.path.join(home, 'a'), 'w') as f: + f.write('a') + os.mkdir(f.name + 'd') + with open(os.path.join(f.name + 'd', 'b'), 'w') as f2: + f2.write('bbb') + os.symlink(f.name, f.name + 'd/s') + os.link(f.name, f.name + 'd/h') + os.symlink('XXX', f.name + 'd/q') + ft.handle_serialized_command(serialized_cmd(action='file', file_id='a', data=b'a')) + ft.handle_serialized_command(serialized_cmd(action='file', file_id='b', data=b'ad')) + files = {r['name']: r for r in ft.test_responses if r['action'] == 'file'} + self.ae(len(files), 6) + q = files[f.name] + tgt = q['status'].encode('ascii') + self.ae(q['size'], 1), self.assertNotIn('ftype', q) + q = files[f.name + 'd'] + self.ae(q['ftype'], 'directory') + q = files[f.name + 'd/b'] + self.ae(q['size'], 3) + q = files[f.name + 'd/s'] + self.ae(q['ftype'], 'symlink') + self.ae(q['data'], tgt) + q = files[f.name + 'd/h'] + self.ae(q['ftype'], 'link') + self.ae(q['data'], tgt) + q = files[f.name + 'd/q'] + self.ae(q['ftype'], 'symlink') + self.assertNotIn('data', q) + def test_file_put(self): # send refusal for quiet in (0, 1, 2):