Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions dvc/tree/ssh/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import os
import posixpath
import shlex
import stat
from contextlib import suppress

Expand Down Expand Up @@ -291,6 +292,7 @@ def md5(self, path):
Example:
MD5 (foo.txt) = f3d220a856b52aabbf294351e8a24300
"""
path = shlex.quote(path)
if self.uname == "Linux":
md5 = self.execute("md5sum " + path).split()[0]
elif self.uname == "Darwin":
Expand All @@ -304,6 +306,8 @@ def md5(self, path):
return md5

def copy(self, src, dest):
dest = shlex.quote(dest)
src = shlex.quote(src)
self.execute(f"cp {src} {dest}")

def open_max_sftp_channels(self):
Expand All @@ -325,6 +329,8 @@ def symlink(self, src, dest):
self.sftp.symlink(src, dest)

def reflink(self, src, dest):
dest = shlex.quote(dest)
src = shlex.quote(src)
if self.uname == "Linux":
return self.execute(f"cp --reflink {src} {dest}")

Expand All @@ -334,4 +340,6 @@ def reflink(self, src, dest):
raise DvcException(f"'{self.uname}' is not supported as a SSH remote")

def hardlink(self, src, dest):
dest = shlex.quote(dest)
src = shlex.quote(src)
self.execute(f"ln {src} {dest}")
66 changes: 66 additions & 0 deletions tests/unit/remote/ssh/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@

from dvc.info import get_fs_type
from dvc.system import System
from dvc.tree.ssh.connection import SSHConnection

here = os.path.abspath(os.path.dirname(__file__))

SRC_PATH_WITH_SPECIAL_CHARACTERS = "Escape me [' , ']"
ESCAPED_SRC_PATH_WITH_SPECIAL_CHARACTERS = "'Escape me ['\"'\"' , '\"'\"']'"

DEST_PATH_WITH_SPECIAL_CHARACTERS = "Escape me too [' , ']"
ESCAPED_DEST_PATH_WITH_SPECIAL_CHARACTERS = (
"'Escape me too ['\"'\"' , '\"'\"']'"
)


def test_isdir(ssh_connection):
assert ssh_connection.isdir(here)
Expand Down Expand Up @@ -131,3 +140,60 @@ def test_move(tmp_dir, ssh_connection):
ssh_connection.move("foo", "copy")
assert os.path.exists("copy")
assert not os.path.exists("foo")


@pytest.mark.parametrize(
"uname,md5command", [("Linux", "md5sum"), ("Darwin", "md5")]
)
def test_escapes_filepaths_for_md5_calculation(
ssh_connection, uname, md5command, mocker
):
fake_md5 = "x" * 32
uname_mock = mocker.PropertyMock(return_value=uname)
mocker.patch.object(SSHConnection, "uname", new_callable=uname_mock)
ssh_connection.execute = mocker.Mock(return_value=fake_md5)
ssh_connection.md5(SRC_PATH_WITH_SPECIAL_CHARACTERS)
ssh_connection.execute.assert_called_with(
f"{md5command} {ESCAPED_SRC_PATH_WITH_SPECIAL_CHARACTERS}"
)


def test_escapes_filepaths_for_copy(ssh_connection, mocker):
ssh_connection.execute = mocker.Mock()
ssh_connection.copy(
SRC_PATH_WITH_SPECIAL_CHARACTERS, DEST_PATH_WITH_SPECIAL_CHARACTERS
)
ssh_connection.execute.assert_called_with(
f"cp {ESCAPED_SRC_PATH_WITH_SPECIAL_CHARACTERS} "
+ f"{ESCAPED_DEST_PATH_WITH_SPECIAL_CHARACTERS}"
)


@pytest.mark.parametrize(
"uname,cp_command", [("Linux", "cp --reflink"), ("Darwin", "cp -c")]
)
def test_escapes_filepaths_for_reflink(
ssh_connection, uname, cp_command, mocker
):
uname_mock = mocker.PropertyMock(return_value=uname)
mocker.patch.object(SSHConnection, "uname", new_callable=uname_mock)
ssh_connection.execute = mocker.Mock()
ssh_connection.reflink(
SRC_PATH_WITH_SPECIAL_CHARACTERS, DEST_PATH_WITH_SPECIAL_CHARACTERS
)
ssh_connection.execute.assert_called_with(
f"{cp_command} "
+ f"{ESCAPED_SRC_PATH_WITH_SPECIAL_CHARACTERS} "
+ f"{ESCAPED_DEST_PATH_WITH_SPECIAL_CHARACTERS}"
)


def test_escapes_filepaths_for_hardlink(ssh_connection, mocker):
ssh_connection.execute = mocker.Mock()
ssh_connection.hardlink(
SRC_PATH_WITH_SPECIAL_CHARACTERS, DEST_PATH_WITH_SPECIAL_CHARACTERS
)
ssh_connection.execute.assert_called_with(
f"ln {ESCAPED_SRC_PATH_WITH_SPECIAL_CHARACTERS} "
+ f"{ESCAPED_DEST_PATH_WITH_SPECIAL_CHARACTERS}"
)