Up-/Download schlagen fehl: pwd() liefert ".", _abs() baut daraus "/./datei" #3

Closed
opened 2026-08-31 17:16:13 +00:00 by x3 · 0 comments
Owner

Symptom

Herunterladen (l) und Hochladen (u) einzelner Dateien scheitern reproduzierbar direkt nach dem Verbinden:

Download: [Errno 2] No such file
Upload:   [Errno 13] Permission denied

Ursache

SFTPBackend.connect() setzt self._cwd = self._sftp.getcwd() or ".". Paramikos getcwd() liefert nach open_sftp() aber None, solange kein explizites chdir() erfolgt ist. _cwd wird damit auf "." gesetzt.

Die App uebernimmt diesen Wert als remote_path und baut daraus via _join(".", "datei.txt", False) den Pfad "./datei.txt". SFTPBackend._abs() praefixt alles, was nicht mit / beginnt, mit einem Slash — Ergebnis: "/./datei.txt".

Der Server normalisiert das zu /datei.txt, also dem Dateisystem-Root statt dem Home-Verzeichnis. Daher No such file beim Download und Permission denied beim Upload.

Das Listing funktioniert trotzdem, weil listdir(".") serverseitig relativ zum Home aufgeloest wird — deshalb faellt der Fehler erst beim Transfer auf.

Reproduktion

from ftptui.sftp_backend import SFTPBackend
from ftptui.app import _join

b = SFTPBackend()
b.connect(host, 22, user, password)
print(repr(b.pwd()))                       # '.'
print(repr(b._sftp.getcwd()))              # None
print(repr(b._sftp.normalize('.')))        # '/home/<user>'  <- korrekt

target = _join(b.pwd(), 'remotefile.txt', False)
print(repr(target), repr(b._abs(target)))  # './remotefile.txt' '/./remotefile.txt'
b.download(target, '/tmp/dl.txt')          # FileNotFoundError

Loesung

connect() muss den Pfad ueber self._sftp.normalize(".") auf einen absoluten Pfad aufloesen, statt auf "." zurueckzufallen. Zusaetzlich sollte _abs() relative Pfade gegen _cwd aufloesen statt blind einen Slash voranzustellen.

Zweiter, unabhaengiger Bug im selben Codepfad

BrowserScreen._copy_tree() (rekursiver Ordner-Transfer) hat die beiden mkdir-Zweige vertauscht:

  • download=True: ruft self.backend.mkdir(dst) auf, obwohl dst ein lokaler Pfad ist -> legt das Verzeichnis auf dem Server an
  • download=False: ruft os.makedirs(dst) auf, obwohl dst ein entfernter Pfad ist -> legt es lokal an

Ordner-Transfers in beide Richtungen sind dadurch defekt.

Betroffen

v0.3.1 und frueher, SFTP-Backend.

## Symptom Herunterladen (`l`) und Hochladen (`u`) einzelner Dateien scheitern reproduzierbar direkt nach dem Verbinden: ``` Download: [Errno 2] No such file Upload: [Errno 13] Permission denied ``` ## Ursache `SFTPBackend.connect()` setzt `self._cwd = self._sftp.getcwd() or "."`. Paramikos `getcwd()` liefert nach `open_sftp()` aber **`None`**, solange kein explizites `chdir()` erfolgt ist. `_cwd` wird damit auf `"."` gesetzt. Die App uebernimmt diesen Wert als `remote_path` und baut daraus via `_join(".", "datei.txt", False)` den Pfad `"./datei.txt"`. `SFTPBackend._abs()` praefixt alles, was nicht mit `/` beginnt, mit einem Slash — Ergebnis: `"/./datei.txt"`. Der Server normalisiert das zu `/datei.txt`, also dem **Dateisystem-Root** statt dem Home-Verzeichnis. Daher `No such file` beim Download und `Permission denied` beim Upload. Das Listing funktioniert trotzdem, weil `listdir(".")` serverseitig relativ zum Home aufgeloest wird — deshalb faellt der Fehler erst beim Transfer auf. ## Reproduktion ```python from ftptui.sftp_backend import SFTPBackend from ftptui.app import _join b = SFTPBackend() b.connect(host, 22, user, password) print(repr(b.pwd())) # '.' print(repr(b._sftp.getcwd())) # None print(repr(b._sftp.normalize('.'))) # '/home/<user>' <- korrekt target = _join(b.pwd(), 'remotefile.txt', False) print(repr(target), repr(b._abs(target))) # './remotefile.txt' '/./remotefile.txt' b.download(target, '/tmp/dl.txt') # FileNotFoundError ``` ## Loesung `connect()` muss den Pfad ueber `self._sftp.normalize(".")` auf einen absoluten Pfad aufloesen, statt auf `"."` zurueckzufallen. Zusaetzlich sollte `_abs()` relative Pfade gegen `_cwd` aufloesen statt blind einen Slash voranzustellen. ## Zweiter, unabhaengiger Bug im selben Codepfad `BrowserScreen._copy_tree()` (rekursiver Ordner-Transfer) hat die beiden `mkdir`-Zweige vertauscht: - `download=True`: ruft `self.backend.mkdir(dst)` auf, obwohl `dst` ein **lokaler** Pfad ist -> legt das Verzeichnis auf dem Server an - `download=False`: ruft `os.makedirs(dst)` auf, obwohl `dst` ein **entfernter** Pfad ist -> legt es lokal an Ordner-Transfers in beide Richtungen sind dadurch defekt. ## Betroffen v0.3.1 und frueher, SFTP-Backend.
x3 closed this issue 2026-08-31 17:33:07 +00:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: x3/ftptui#3