Repository: iterative/dvc

Issue:
Paths break with dvc push from Windows to Linux (ssh)
# Bug Report

<!--
## Issue name

Issue names must follow the pattern `command: description` where the command is the dvc command that you are trying to run. The description should describe the consequence of the bug. 

Example: `repro: doesn't detect input changes`
-->

## Description

When pushing via SSH/SFTP from Windows (MinGW32) to an SSH server running on Linux, there seems to be a problem with forward and backward slashes in paths. The `.dvc/config` on the Windows client contains

```
['remote "storage"']
    url = ssh://user@machine/home/user/dvc_root
```

After pushing, the DVC files don't end up at `/home/user/dvc_root` on the server, however. Instead, the `/home/user` directory contains all of the DVC files directly (note the backslashes):

```
$ ls /home/user | more

\home\user\dvc_root\00
\home\user\dvc_root\00\00dad9454dc25f899090b21b3457d7
\home\user\dvc_root\00\014cf75413925c486fb0d51c4d79c3
\home\user\dvc_root\00\01ad837abddba70b1452864f4bf742
```

What went wrong here? In the above example, I would expect a file `00dad9454dc25f899090b21b3457d7` in `/home/user/dvc_root/00`, not a file `\home\user\dvc_root\00\00dad9454dc25f899090b21b3457d7` in `/home/user`.

### Reproduce

1. Set up a regular ssh server on a Linux machine, no special configuration
2. Configure it as a DVC remote for a repo on a Windows machine
3. Push from the Windows machine to the Linux remote

### Expected

<!--
A clear and concise description of what you expect to happen.
-->

### Environment information

<!--
This is required to ensure that we can reproduce the bug.
-->

**Output of `dvc doctor`:**

Linux server:

```console
$ dvc doctor

DVC version: 2.5.1 (pip)
---------------------------------
Platform: Python 3.6.9 on Linux-5.4.0-81-generic-x86_64-with-Ubuntu-18.04-bionic
Supports:
	hdfs (pyarrow = 4.0.1),
	http (requests = 2.25.1),
	https (requests = 2.25.1)
```
 
Windows client:
```console
 $ dvc doctor
DVC version: 2.6.4 (pip)
---------------------------------
Platform: Python 3.7.5 on Windows-10-10.0.19041-SP0
Supports:
        http (requests = 2.26.0),
        https (requests = 2.26.0),
        ssh (sshfs = 2021.7.1)
```



Discussion:
CC @isidentical , could it be https://github.com/iterative/dvc/blob/b5fe9b459658bca9e9c5963b468e1d1fe18b7c15/dvc/fs/ssh.py#L41 ?

Resolving patch:
diff --git a/dvc/fs/ssh.py b/dvc/fs/ssh.py
--- a/dvc/fs/ssh.py
+++ b/dvc/fs/ssh.py
@@ -39,7 +39,7 @@ def _with_bucket(self, path):
             path_str = path.path
         else:
             path_str = super()._with_bucket(path)
-        return os.path.normpath(path_str)
+        return path_str
 
     def _prepare_credentials(self, **config):
         self.CAN_TRAVERSE = True


Test patch:
diff --git a/tests/unit/fs/test_ssh.py b/tests/unit/fs/test_ssh.py
--- a/tests/unit/fs/test_ssh.py
+++ b/tests/unit/fs/test_ssh.py
@@ -178,3 +178,23 @@ def test_ssh_multi_identity_files(mock_file):
 def test_ssh_gss_auth(mock_file, config, expected_gss_auth):
     fs = SSHFileSystem(**config)
     assert fs.fs_args["gss_auth"] == expected_gss_auth
+
+
+@pytest.mark.parametrize(
+    "url, path",
+    [
+        ("ssh://a@b/", "/"),
+        ("ssh://a@b:/", "/"),
+        ("ssh://a@b/home", "/home"),
+        ("ssh://a@b:/home", "/home"),
+        ("ssh://a@b/home/tmp", "/home/tmp"),
+        ("ssh://a@b:/home/tmp", "/home/tmp"),
+        ("ssh://a@b/home/tmp/something", "/home/tmp/something"),
+        ("ssh://a@b:/home/tmp/something", "/home/tmp/something"),
+    ],
+)
+def test_ssh_fs_path_management(url, path):
+    fs = SSHFileSystem(host="x.y.z")
+
+    path_info = fs.PATH_CLS(url)
+    assert fs._with_bucket(path_info) == path