sftp: misc fixes + add to docker-im

This commit is contained in:
ed 2026-01-02 15:18:11 +00:00
parent 4e9cf95e8d
commit ec7ea30951
4 changed files with 30 additions and 13 deletions

View file

@ -1408,7 +1408,7 @@ goes roughly 700 MiB/s (slower than webdav and ftp)
the sftp-server requires the optional dependency [paramiko](https://pypi.org/project/paramiko/); the sftp-server requires the optional dependency [paramiko](https://pypi.org/project/paramiko/);
* if you are **not** using docker, then install paramiko somehow * if you are **not** using docker, then install paramiko somehow
* if you **are** using docker, then use one of the following image variants: `ac` / `iv` / `dj` * if you **are** using docker, then use one of the following image variants: `ac` / `im` / `iv` / `dj`
enable sftpd with `--sftp 3922` to listen on port 3922; enable sftpd with `--sftp 3922` to listen on port 3922;
* use global-option `sftp-key` to associate an ssh-key with a user; * use global-option `sftp-key` to associate an ssh-key with a user;

View file

@ -14,7 +14,13 @@ import paramiko
import paramiko.common import paramiko.common
import paramiko.sftp_attr import paramiko.sftp_attr
from paramiko.common import AUTH_FAILED, AUTH_SUCCESSFUL from paramiko.common import AUTH_FAILED, AUTH_SUCCESSFUL
from paramiko.sftp import SFTP_FAILURE, SFTP_OK, SFTP_PERMISSION_DENIED from paramiko.sftp import (
SFTP_FAILURE,
SFTP_NO_SUCH_FILE,
SFTP_OK,
SFTP_OP_UNSUPPORTED,
SFTP_PERMISSION_DENIED,
)
from .__init__ import ANYWIN, TYPE_CHECKING from .__init__ import ANYWIN, TYPE_CHECKING
from .authsrv import LEELOO_DALLAS, VFS, AuthSrv from .authsrv import LEELOO_DALLAS, VFS, AuthSrv
@ -41,8 +47,7 @@ if TYPE_CHECKING:
from .svchub import SvcHub from .svchub import SvcHub
if True: # pylint: disable=using-constant-test if True: # pylint: disable=using-constant-test
import typing from typing import Any, BinaryIO, Optional, Union
from typing import Any, Optional, Union
SATTR = paramiko.sftp_attr.SFTPAttributes SATTR = paramiko.sftp_attr.SFTPAttributes
@ -247,11 +252,17 @@ class SSH_Srv(paramiko.ServerInterface):
class SFTP_FH(paramiko.SFTPHandle): class SFTP_FH(paramiko.SFTPHandle):
def __init__(self, flags: int = 0) -> None:
self.filename = ""
self.readfile: Optional[BinaryIO] = None
self.writefile: Optional[BinaryIO] = None
super(SFTP_FH, self).__init__(flags)
def stat(self): def stat(self):
try: try:
return SATTR.from_stat(os.fstat(self.readfile.fileno())) f = self.readfile or self.writefile
return SATTR.from_stat(os.fstat(f.fileno()))
except OSError as ex: except OSError as ex:
print("a", repr(ex))
return paramiko.SFTPServer.convert_errno(ex.errno) return paramiko.SFTPServer.convert_errno(ex.errno)
def chattr(self, attr): def chattr(self, attr):
@ -332,16 +343,21 @@ class SFTP_Srv(paramiko.SFTPServerInterface):
def list_folder(self, path: str) -> list[SATTR] | int: def list_folder(self, path: str) -> list[SATTR] | int:
try: try:
return self._list_folder(path) return self._list_folder(path)
except Pebkac as ex:
if ex.code == 404:
self.log("folder 404: %s" % (path,))
return SFTP_NO_SUCH_FILE
return SFTP_PERMISSION_DENIED
except: except:
self.log("unhandled exception: %s" % (min_ex(),), 1) self.log("unhandled exception: %s" % (min_ex(),), 1)
return SFTP_FAILURE return SFTP_FAILURE
def _list_folder(self, path: str) -> list[SATTR] | int: def _list_folder(self, path: str) -> list[SATTR] | int:
try: try:
ap, vn, rem = self.v2a(path, True, False, False, False) ap, vn, rem = self.v2a(path, r=True)
except Pebkac: except Pebkac:
try: try:
self.v2a(path, False, True, False, False) self.v2a(path, w=True)
return [] # display write-only folders as empty return [] # display write-only folders as empty
except: except:
pass pass
@ -396,12 +412,12 @@ class SFTP_Srv(paramiko.SFTPServerInterface):
def _stat(self, vp: str) -> SATTR | int: def _stat(self, vp: str) -> SATTR | int:
try: try:
ap = self.v2a(vp, True, False, False, False)[0] ap = self.v2a(vp, r=True)[0]
st = bos.stat(ap) st = bos.stat(ap)
except: except:
if vp.strip("/") or self.asrv.vfs.realpath: if vp.strip("/") or self.asrv.vfs.realpath:
try: try:
self.v2a(vp, False, True, False, False)[0] self.v2a(vp, w=True)[0]
except: except:
return SFTP_PERMISSION_DENIED return SFTP_PERMISSION_DENIED
zi = int(time.time()) zi = int(time.time())
@ -634,7 +650,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface):
return paramiko.SFTPServer.convert_errno(ex.errno) return paramiko.SFTPServer.convert_errno(ex.errno)
def symlink(self, target_path: str, path: str) -> int: def symlink(self, target_path: str, path: str) -> int:
return paramiko.SFTPServer.SFTP_OP_UNSUPPORTED return SFTP_OP_UNSUPPORTED
def readlink(self, path: str) -> str | int: def readlink(self, path: str) -> str | int:
return path return path

View file

@ -1148,7 +1148,7 @@ class Up2k(object):
ft = "\033[0;32m{}{:.0}" ft = "\033[0;32m{}{:.0}"
ff = "\033[0;35m{}{:.0}" ff = "\033[0;35m{}{:.0}"
fv = "\033[0;36m{}:\033[90m{}" fv = "\033[0;36m{}:\033[90m{}"
zs = "bcasechk du_iwho emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v" zs = "bcasechk du_iwho emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s ls_q_m put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
fx = set(zs.split()) fx = set(zs.split())
fd = vf_bmap() fd = vf_bmap()
fd.update(vf_cmap()) fd.update(vf_cmap())

View file

@ -9,7 +9,8 @@ ENV XDG_CONFIG_HOME=/cfg
RUN apk --no-cache add !pyc \ RUN apk --no-cache add !pyc \
tzdata wget mimalloc2 mimalloc2-insecure \ tzdata wget mimalloc2 mimalloc2-insecure \
py3-jinja2 py3-argon2-cffi py3-openssl py3-pillow py3-mutagen py3-jinja2 py3-argon2-cffi \
py3-openssl py3-paramiko py3-pillow py3-mutagen
COPY i/dist/copyparty-sfx.py innvikler.sh ./ COPY i/dist/copyparty-sfx.py innvikler.sh ./
ADD base ./base ADD base ./base