diff --git a/src/smbclient/_io.py b/src/smbclient/_io.py index 539c40d..19d8a9c 100644 --- a/src/smbclient/_io.py +++ b/src/smbclient/_io.py @@ -381,7 +381,11 @@ class SMBRawIO(io.RawIOBase): def __init__( self, path, mode="r", share_access=None, desired_access=None, file_attributes=None, create_options=0, **kwargs ): - tree, fd_path = get_smb_tree(path, **kwargs) + # get_smb_tree runs outside the SMBFileTransaction layer that normally translates these. + try: + tree, fd_path = get_smb_tree(path, **kwargs) + except SMBResponseException as exc: + raise SMBOSError(exc.status, path) from exc self.share_access = share_access self.fd = Open(tree, fd_path) diff --git a/tests/test_smbclient_os.py b/tests/test_smbclient_os.py index e1387e2..6a34958 100644 --- a/tests/test_smbclient_os.py +++ b/tests/test_smbclient_os.py @@ -2186,3 +2186,14 @@ def test_dfs_nonexisting_path(smb_dfs_share): with pytest.raises(SMBOSError): smbclient.lstat(fake_file) + + +def test_open_file_missing_share_raises_os_error(smb_real): + # A missing share fails tree connect with STATUS_BAD_NETWORK_NAME, a non-OSError + # that must surface as SMBOSError so except OSError / ignore_errors callers catch it. + bad_path = rf"\\{smb_real[2]}\does_not_exist\file.txt" + + with pytest.raises(SMBOSError) as exc_info: + smbclient.open_file(bad_path, username=smb_real[0], password=smb_real[1], port=smb_real[3]) + + assert exc_info.value.filename == bad_path diff --git a/tests/test_smbclient_pool.py b/tests/test_smbclient_pool.py index 02c5e48..9bd2bee 100644 --- a/tests/test_smbclient_pool.py +++ b/tests/test_smbclient_pool.py @@ -8,7 +8,13 @@ import smbclient._io as io import smbclient._pool as pool from smbprotocol.dfs import DFSReferralResponse -from smbprotocol.exceptions import BadNetworkName, InvalidParameter, ObjectPathNotFound +from smbprotocol.exceptions import ( + BadNetworkName, + InvalidParameter, + ObjectPathNotFound, + SMBOSError, +) +from smbprotocol.header import NtStatus from .conftest import DC_REFERRAL, DOMAIN_NAME, DOMAIN_REFERRAL @@ -150,3 +156,16 @@ def test_resolve_dfs_referral_no_links(reset_config, monkeypatch, mocker): with pytest.raises(ObjectPathNotFound): list(io._resolve_dfs(raw_io)) + + +def test_raw_io_translates_smb_response_to_os_error(mocker): + # Anything escaping get_smb_tree here must surface as OSError so the + # rmtree, remove, rmdir, and scandir error paths route it correctly. + mocker.patch.object(io, "get_smb_tree", side_effect=BadNetworkName()) + + path = r"\\server\share\missing" + with pytest.raises(SMBOSError) as exc_info: + io.SMBRawIO(path) + + assert exc_info.value.ntstatus == NtStatus.STATUS_BAD_NETWORK_NAME + assert exc_info.value.filename == path