diff --git a/ftptui/app.py b/ftptui/app.py index a4f952d..8d276e0 100644 --- a/ftptui/app.py +++ b/ftptui/app.py @@ -450,7 +450,8 @@ class BrowserScreen(Screen): def _copy_tree(self, src: str, dst: str, download: bool) -> None: try: if download: - self.backend.mkdir(dst) + # dst ist ein *lokaler* Pfad -> lokal anlegen, nicht auf dem Server + os.makedirs(dst, exist_ok=True) entries = self.backend.listdir(src) for e in entries: if e.kind == "dir": @@ -460,7 +461,8 @@ class BrowserScreen(Screen): else: self.backend.download(_join(src, e.name, False), _join(dst, e.name, True)) else: - os.makedirs(dst, exist_ok=True) + # dst ist ein *entfernter* Pfad -> auf dem Server anlegen + self.backend.mkdir(dst) for item in os.scandir(src): if item.is_dir(): self._copy_tree( diff --git a/ftptui/sftp_backend.py b/ftptui/sftp_backend.py index 979a75a..fc88e8c 100644 --- a/ftptui/sftp_backend.py +++ b/ftptui/sftp_backend.py @@ -29,7 +29,21 @@ class SFTPBackend: timeout=30, ) self._sftp = self._ssh.open_sftp() - self._cwd = self._sftp.getcwd() or "." + self._cwd = self._resolve(".") + + def _resolve(self, path: str) -> str: + """Löst einen Pfad serverseitig zu einem absoluten Pfad auf. + + ``paramiko.SFTPClient.getcwd()`` liefert ``None``, solange kein + explizites ``chdir()`` erfolgt ist – ein Rückfall auf ``"."`` würde + später zu Pfaden wie ``/./datei`` führen, die der Server relativ zum + Dateisystem-Root statt zum Home-Verzeichnis auflöst. + """ + assert self._sftp is not None + try: + return self._sftp.normalize(path) + except OSError: + return path if path.startswith("/") else "/" def listdir(self, path: str) -> list[RemoteEntry]: assert self._sftp is not None @@ -45,22 +59,23 @@ class SFTPBackend: modified=attr.st_mtime, ) ) - self._cwd = path + self._cwd = self._resolve(path) return entries def chdir(self, path: str) -> None: assert self._sftp is not None self._sftp.chdir(path) - self._cwd = self._sftp.getcwd() or path + self._cwd = self._resolve(".") def pwd(self) -> str: return self._cwd def _abs(self, path: str) -> str: - """Baut einen absolut wertigen Pfad relativ zum SFTP-Root.""" - if path.startswith("/"): - return path or "/" - return "/" + path + """Baut einen absoluten Pfad – relative Pfade gelten zum aktuellen CWD.""" + if not path: + return self._cwd or "/" + base = self._cwd if self._cwd.startswith("/") else "/" + return str(PurePosixPath(base) / path) def download(self, remote: str, local: str) -> None: assert self._sftp is not None