Skip to content

Commit

Permalink
Smb non standard port (#1293)
Browse files Browse the repository at this point in the history
* Add support for non-standard samba ports

* Add tests for standard and non-standard ports in smb backend
  • Loading branch information
ondave authored Jun 20, 2023
1 parent aff3f42 commit 348d4ab
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 24 deletions.
50 changes: 31 additions & 19 deletions fsspec/implementations/smb.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,23 +139,23 @@ def _get_kwargs_from_urls(path):
def mkdir(self, path, create_parents=True, **kwargs):
wpath = _as_unc_path(self.host, path)
if create_parents:
smbclient.makedirs(wpath, exist_ok=False, **kwargs)
smbclient.makedirs(wpath, exist_ok=False, port=self.port, **kwargs)
else:
smbclient.mkdir(wpath, **kwargs)
smbclient.mkdir(wpath, port=self.port, **kwargs)

def makedirs(self, path, exist_ok=False):
if _share_has_path(path):
wpath = _as_unc_path(self.host, path)
smbclient.makedirs(wpath, exist_ok=exist_ok)
smbclient.makedirs(wpath, exist_ok=exist_ok, port=self.port)

def rmdir(self, path):
if _share_has_path(path):
wpath = _as_unc_path(self.host, path)
smbclient.rmdir(wpath)
smbclient.rmdir(wpath, port=self.port)

def info(self, path, **kwargs):
wpath = _as_unc_path(self.host, path)
stats = smbclient.stat(wpath, **kwargs)
stats = smbclient.stat(wpath, port=self.port, **kwargs)
if S_ISDIR(stats.st_mode):
stype = "directory"
elif S_ISLNK(stats.st_mode):
Expand All @@ -176,18 +176,18 @@ def info(self, path, **kwargs):
def created(self, path):
"""Return the created timestamp of a file as a datetime.datetime"""
wpath = _as_unc_path(self.host, path)
stats = smbclient.stat(wpath)
stats = smbclient.stat(wpath, port=self.port)
return datetime.datetime.utcfromtimestamp(stats.st_ctime)

def modified(self, path):
"""Return the modified timestamp of a file as a datetime.datetime"""
wpath = _as_unc_path(self.host, path)
stats = smbclient.stat(wpath)
stats = smbclient.stat(wpath, port=self.port)
return datetime.datetime.utcfromtimestamp(stats.st_mtime)

def ls(self, path, detail=True, **kwargs):
unc = _as_unc_path(self.host, path)
listed = smbclient.listdir(unc, **kwargs)
listed = smbclient.listdir(unc, port=self.port, **kwargs)
dirs = ["/".join([path.rstrip("/"), p]) for p in listed]
if detail:
dirs = [self.info(d) for d in dirs]
Expand Down Expand Up @@ -217,30 +217,37 @@ def _open(
share_access = kwargs.pop("share_access", self.share_access)
if "w" in mode and autocommit is False:
temp = _as_temp_path(self.host, path, self.temppath)
return SMBFileOpener(wpath, temp, mode, block_size=bls, **kwargs)
return SMBFileOpener(
wpath, temp, mode, port=self.port, block_size=bls, **kwargs
)
return smbclient.open_file(
wpath, mode, buffering=bls, share_access=share_access, **kwargs
wpath,
mode,
buffering=bls,
share_access=share_access,
port=self.port,
**kwargs,
)

def copy(self, path1, path2, **kwargs):
"""Copy within two locations in the same filesystem"""
wpath1 = _as_unc_path(self.host, path1)
wpath2 = _as_unc_path(self.host, path2)
smbclient.copyfile(wpath1, wpath2, **kwargs)
smbclient.copyfile(wpath1, wpath2, port=self.port, **kwargs)

def _rm(self, path):
if _share_has_path(path):
wpath = _as_unc_path(self.host, path)
stats = smbclient.stat(wpath)
stats = smbclient.stat(wpath, port=self.port)
if S_ISDIR(stats.st_mode):
smbclient.rmdir(wpath)
smbclient.rmdir(wpath, port=self.port)
else:
smbclient.remove(wpath)
smbclient.remove(wpath, port=self.port)

def mv(self, path1, path2, **kwargs):
wpath1 = _as_unc_path(self.host, path1)
wpath2 = _as_unc_path(self.host, path2)
smbclient.rename(wpath1, wpath2, **kwargs)
smbclient.rename(wpath1, wpath2, port=self.port, **kwargs)


def _as_unc_path(host, path):
Expand All @@ -266,30 +273,35 @@ def _share_has_path(path):
class SMBFileOpener(object):
"""writes to remote temporary file, move on commit"""

def __init__(self, path, temp, mode, block_size=-1, **kwargs):
def __init__(self, path, temp, mode, port=445, block_size=-1, **kwargs):
self.path = path
self.temp = temp
self.mode = mode
self.block_size = block_size
self.kwargs = kwargs
self.smbfile = None
self._incontext = False
self.port = port
self._open()

def _open(self):
if self.smbfile is None or self.smbfile.closed:
self.smbfile = smbclient.open_file(
self.temp, self.mode, buffering=self.block_size, **self.kwargs
self.temp,
self.mode,
port=self.port,
buffering=self.block_size,
**self.kwargs,
)

def commit(self):
"""Move temp file to definitive on success."""
# TODO: use transaction support in SMB protocol
smbclient.replace(self.temp, self.path)
smbclient.replace(self.temp, self.path, port=self.port)

def discard(self):
"""Remove the temp file on failure."""
smbclient.remove(self.temp)
smbclient.remove(self.temp, port=self.port)

def __fspath__(self):
return self.path
Expand Down
18 changes: 13 additions & 5 deletions fsspec/implementations/tests/test_smb.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

# ! pylint: disable=redefined-outer-name,missing-function-docstring

# Test standard and non-standard ports
port_test = [445, 9999]


def stop_docker(container):
cmd = shlex.split('docker ps -a -q --filter "name=%s"' % container)
Expand All @@ -24,8 +27,8 @@ def stop_docker(container):
subprocess.call(["docker", "rm", "-f", "-v", cid])


@pytest.fixture(scope="module")
def smb_params():
@pytest.fixture(scope="module", params=port_test)
def smb_params(request):
try:
pchk = ["docker", "run", "--name", "fsspec_test_smb", "hello-world"]
subprocess.check_call(pchk)
Expand All @@ -37,15 +40,20 @@ def smb_params():
# requires docker
container = "fsspec_smb"
stop_docker(container)
img = "docker run --name {} --detach -p 139:139 -p 445:445 dperson/samba"
img = "docker run --name {} --detach -p 139:139 -p {}:445 dperson/samba"
cfg = " -p -u 'testuser;testpass' -s 'home;/share;no;no;no;testuser'"
cmd = img.format(container) + cfg
cmd = img.format(container, request.param) + cfg
cid = subprocess.check_output(shlex.split(cmd)).strip().decode()
logger = logging.getLogger("fsspec")
logger.debug("Container: %s", cid)
try:
time.sleep(1)
yield dict(host="localhost", port=445, username="testuser", password="testpass")
yield dict(
host="localhost",
port=request.param,
username="testuser",
password="testpass",
)
finally:
import smbclient # pylint: disable=import-outside-toplevel

Expand Down

0 comments on commit 348d4ab

Please sign in to comment.