# -*- coding: utf-8 -*- # Copyright: (c) 2019, Jordan Borean (@jborean93) <jborean93@gmail.com> # MIT License (see LICENSE or https://opensource.org/licenses/MIT) from __future__ import division import io import locale import ntpath import os import pytest import re import smbclient # Tests that we expose this in smbclient/__init__.py import stat from smbclient._io import ( query_info, SMBFileTransaction, ) from smbclient._os import ( SMBDirectoryIO, SMBDirEntry, SMBFileIO, ) from smbprotocol.exceptions import ( SMBAuthenticationError, SMBOSError, ) from smbprotocol.file_info import ( FileAttributes, FileStreamInformation, ) from smbprotocol.reparse_point import ( ReparseDataBuffer, ReparseTags, ) HAS_SSPI = False try: import sspi HAS_SSPI = True except ImportError: pass @pytest.mark.parametrize('path', [ '\\\\only_server', '\\\\server_slash\\', ]) def test_open_bad_path(path): expected = "The SMB path specified must contain the server and share to connect to" with pytest.raises(ValueError, match=expected): smbclient.open_file(path) def test_reset_connection(smb_share): smbclient.reset_connection_cache() # Once we've reset the connection it should fail because we didn't set any credentials. # Won't work if pywin32 is installed as implicit auth is available. if not HAS_SSPI: expected = 'Failed to authenticate with server' with pytest.raises(SMBAuthenticationError, match=expected): smbclient.stat(smb_share) def test_delete_session(smb_share): server = ntpath.normpath(smb_share).split("\\")[2] smbclient.delete_session(server) # Once we've closed the connection it should fail because we didn't set any credentials if not HAS_SSPI: expected = 'Failed to authenticate with server' with pytest.raises(SMBAuthenticationError, match=expected): smbclient.stat(smb_share) def test_copy_across_paths_raises(smb_share): expected = "Cannot copy a file to a different root than the src." with pytest.raises(ValueError, match=re.escape(expected)): smbclient.copyfile("%s\\file" % smb_share, "//host/filer2/file2") def test_copyfile_src_not_unc(): expected = "src must be an absolute path to where the file should be copied from." with pytest.raises(ValueError, match=re.escape(expected)): smbclient.copyfile("file", "\\\\server\\share\\file") def test_copyfile_dst_not_unc(): expected = "dst must be an absolute path to where the file should be copied to." with pytest.raises(ValueError, match=re.escape(expected)): smbclient.copyfile("\\\\server\\share\\file", "file") def test_server_side_copy_multiple_chunks(smb_share): smbclient.mkdir("%s\\dir2" % smb_share) with smbclient.open_file("%s\\file1" % smb_share, mode='w') as fd: fd.write(u"content" * 1024) smbclient._os.CHUNK_SIZE = 1024 smbclient.copyfile("%s\\file1" % smb_share, "%s\\dir2\\file1" % smb_share) src_stat = smbclient.stat("%s\\file1" % smb_share) dst_stat = smbclient.stat("%s\\dir2\\file1" % smb_share) assert src_stat.st_size == dst_stat.st_size def test_server_side_copy_large_file(smb_share): src_filename = "%s\\file1" % smb_share dst_filename = "%s\\file2" % smb_share # Actually reading and writing more than 16MB takes too long for the tests so just test the file length expected_length = 1024 * 1024 * 17 with smbclient.open_file(src_filename, mode='wb') as fd: fd.truncate(expected_length) smbclient.copyfile(src_filename, dst_filename) smbclient.copyfile(src_filename, dst_filename) with smbclient.open_file(dst_filename, mode='rb') as fd: assert fd.seek(0, smbclient.SEEK_END) assert fd.tell() == expected_length def test_link_relative_path_fail(smb_share): expected = "src must be the absolute path to where the file is hard linked to." with pytest.raises(ValueError, match=expected): smbclient.link("file", smb_share) def test_link_different_root_fail(smb_share): expected = "Cannot hardlink a file to a different root than the src." with pytest.raises(ValueError, match=expected): smbclient.link("\\\\other\\share\\file.txt", smb_share) def test_link_dir_fail(smb_share): expected = "[NtStatus 0xc00000ba] Is a directory" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.link(smb_share, ntpath.join(smb_share, 'file.txt')) def test_link_existing_file_failed(smb_share): file_data = u"content" link_src = ntpath.join(smb_share, 'src.txt') with smbclient.open_file(link_src, mode='w') as fd: fd.write(file_data) link_dst = ntpath.join(smb_share, 'dst.txt') with smbclient.open_file(link_dst, mode='w') as fd: fd.write(file_data) expected = "[NtStatus 0xc0000035] File exists:" with pytest.raises(OSError, match=re.escape(expected)): smbclient.link(link_src, link_dst) def test_link_missing_src_fail(smb_share): link_src = ntpath.join(smb_share, 'src.txt') link_dst = ntpath.join(smb_share, 'dst.txt') expected = "[NtStatus 0xc0000034] No such file or directory" with pytest.raises(OSError, match=re.escape(expected)): smbclient.link(link_src, link_dst) def test_link_to_file(smb_share): file_data = u"content" link_src = ntpath.join(smb_share, 'src.txt') with smbclient.open_file(link_src, mode='w') as fd: fd.write(file_data) link_dst = ntpath.join(smb_share, 'dst.txt') smbclient.link(link_src, link_dst) with smbclient.open_file(link_dst, mode='r') as fd: actual_data = fd.read() assert actual_data == file_data src_stat = smbclient.stat(link_src) dst_stat = smbclient.stat(link_dst) assert src_stat.st_ino == dst_stat.st_ino assert src_stat.st_dev == dst_stat.st_dev assert src_stat.st_nlink == 2 assert dst_stat.st_nlink == 2 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_link_to_symbolic_link_follows(smb_share): normal_filename = "%s\\file.txt" % smb_share link_filename = "%s\\link.txt" % smb_share hard_filename = "%s\\hard.txt" % smb_share with smbclient.open_file(normal_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(normal_filename, link_filename) smbclient.link(link_filename, hard_filename) actual_normal = smbclient.lstat(normal_filename) actual_hard = smbclient.lstat(normal_filename) assert actual_hard.st_ino == actual_normal.st_ino @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_link_to_symbolic_link_not_follows(smb_share): normal_filename = "%s\\file.txt" % smb_share link_filename = "%s\\link.txt" % smb_share hard_filename = "%s\\hard.txt" % smb_share with smbclient.open_file(normal_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(normal_filename, link_filename) smbclient.link(link_filename, hard_filename, follow_symlinks=False) actual_link = smbclient.lstat(normal_filename) actual_hard = smbclient.lstat(normal_filename) assert actual_hard.st_ino == actual_link.st_ino @pytest.mark.parametrize('dirpath, ntstatus', [ ('missing', '0xc0000034'), ('missing\\missing_sub', '0xc000003a'), ]) def test_listdir_missing(dirpath, ntstatus, smb_share): expected = "[NtStatus %s] No such file or directory" % ntstatus with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.listdir(ntpath.join(smb_share, dirpath)) def test_listdir_file_fail(smb_share): filename = ntpath.join(smb_share, "file.txt") with smbclient.open_file(filename, mode='w') as fd: fd.write(u"data") expected = "[NtStatus 0xc0000103] Not a directory" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.listdir(filename) @pytest.mark.parametrize('dirname', [ ('',), # Requires different special perms on Windows, this makes sure we can still enum the root share. ('subdir',), ]) def test_listdir(dirname, smb_share): dirpath = ntpath.join(smb_share, dirname[0]) smbclient.makedirs(dirpath, exist_ok=True) for name in ['file.txt', u'unicode †[💩].txt']: with smbclient.open_file(ntpath.join(dirpath, name), mode='w') as fd: fd.write(u"content") for name in ['subdir1', 'subdir2', u'unicode dir †[💩]', 'subdir1\\sub']: smbclient.mkdir(ntpath.join(dirpath, name)) actual = smbclient.listdir(dirpath) assert len(actual) == 5 assert u'unicode †[💩].txt' in actual assert u'unicode dir †[💩]' in actual assert u'subdir2' in actual assert u'subdir1' in actual assert u'file.txt' in actual def test_listdir_with_pattern(smb_share): for filename in ["file.txt", "file-test1.txt", "file-test1a.txt"]: with smbclient.open_file("%s\\%s" % (smb_share, filename), mode="w") as fd: fd.write(u"content") actual = smbclient.listdir(smb_share, search_pattern="file-test*.txt") assert len(actual) == 2 assert "file-test1.txt" in actual assert "file-test1a.txt" in actual assert smbclient.listdir(smb_share, search_pattern="file-test?.txt") == ["file-test1.txt"] def test_listdir_with_pattern_no_match(smb_share): actual = smbclient.listdir(smb_share, search_pattern="no matching file") assert actual == [] def test_lstat_on_file(smb_share): filename = ntpath.join(smb_share, 'file.txt') with smbclient.open_file(filename, mode='w') as fd: fd.write(u"Content") actual = smbclient.lstat(filename) assert isinstance(actual, smbclient.SMBStatResult) assert actual.st_atime == actual.st_atime_ns / 1000000000 assert actual.st_mtime == actual.st_mtime_ns / 1000000000 assert actual.st_ctime == actual.st_ctime_ns / 1000000000 assert actual.st_chgtime == actual.st_chgtime_ns / 1000000000 assert actual.st_dev is not None assert actual.st_file_attributes == FileAttributes.FILE_ATTRIBUTE_ARCHIVE assert actual.st_gid == 0 assert actual.st_uid == 0 assert actual.st_ino is not None assert actual.st_mode == stat.S_IFREG | 0o666 assert actual.st_nlink == 1 assert actual.st_size == 7 assert actual.st_uid == 0 assert actual.st_reparse_tag == 0 def test_lstat_on_dir(smb_share): dirname = ntpath.join(smb_share, 'dir') smbclient.mkdir(dirname) actual = smbclient.lstat(dirname) assert isinstance(actual, smbclient.SMBStatResult) assert actual.st_atime == actual.st_atime_ns / 1000000000 assert actual.st_mtime == actual.st_mtime_ns / 1000000000 assert actual.st_ctime == actual.st_ctime_ns / 1000000000 assert actual.st_chgtime == actual.st_chgtime_ns / 1000000000 assert actual.st_dev is not None assert actual.st_file_attributes == FileAttributes.FILE_ATTRIBUTE_DIRECTORY assert actual.st_gid == 0 assert actual.st_uid == 0 assert actual.st_ino is not None assert actual.st_mode == stat.S_IFDIR | 0o777 assert actual.st_nlink == 1 assert actual.st_size == 0 assert actual.st_uid == 0 assert actual.st_reparse_tag == 0 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_lstat_on_symlink_file(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) actual_src = smbclient.stat(src_filename) actual = smbclient.lstat(dst_filename) assert actual.st_ino != actual_src.st_ino assert stat.S_ISLNK(actual.st_mode) assert actual.st_reparse_tag == ReparseTags.IO_REPARSE_TAG_SYMLINK def test_mkdir(smb_share): dirname = ntpath.join(smb_share, 'dir') smbclient.mkdir(dirname) actual = smbclient.stat(dirname) assert stat.S_ISDIR(actual.st_mode) expected = "[NtStatus 0xc0000035] File exists:" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.mkdir(dirname) def test_mkdir_missing_parent_fail(smb_share): dirname = ntpath.join(smb_share, 'dir', 'subdir') expected = "[NtStatus 0xc000003a] No such file or directory" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.mkdir(dirname) def test_mkdir_path_is_file_fail(smb_share): filename = ntpath.join(smb_share, 'test.txt') with smbclient.open_file(filename, mode='w') as fd: fd.write(u"content") expected = "[NtStatus 0xc0000035] File exists:" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.mkdir(filename) def test_makedirs_existing_parent(smb_share): dirpath = ntpath.join(smb_share, 'folder') smbclient.makedirs(dirpath) assert smbclient.listdir(smb_share) == ['folder'] def test_makedirs_exist_ok(smb_share): dirpath = ntpath.join(smb_share, 'folder') smbclient.makedirs(dirpath) expected = "[NtStatus 0xc0000035] File exists:" with pytest.raises(OSError, match=re.escape(expected)): smbclient.makedirs(dirpath) smbclient.makedirs(dirpath, exist_ok=True) def test_makedirs_missing_parents(smb_share): dirpath = ntpath.join(smb_share, 'missing', 'missing', 'folder') smbclient.makedirs(dirpath) assert stat.S_ISDIR(smbclient.stat(dirpath).st_mode) def test_makedirs_file_as_parent(smb_share): filepath = ntpath.join(smb_share, 'file.txt') with smbclient.open_file(filepath, 'w') as fd: fd.write(u"text") expected = "[NtStatus 0xc0000035] File exists:" with pytest.raises(OSError, match=re.escape(expected)): smbclient.makedirs(filepath) dirpath = ntpath.join(filepath, 'folder') expected = "[NtStatus 0xc000003a] No such file or directory:" with pytest.raises(OSError, match=re.escape(expected)): smbclient.makedirs(dirpath) def test_read_text_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = u"File Contents\nNewline" expected = "[NtStatus 0xc0000034] No such file or directory" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.open_file(file_path, mode='rb') with smbclient.open_file(file_path, mode='wb') as fd: fd.write(file_contents.encode('utf-8')) with smbclient.open_file(file_path) as fd: assert isinstance(fd, io.TextIOWrapper) assert fd.closed is False assert fd.encoding == locale.getpreferredencoding() assert fd.errors == 'strict' assert fd.line_buffering is False assert fd.name == file_path assert fd.newlines is None actual = fd.read() assert actual == file_contents actual = fd.read() assert actual == "" fd.seek(0) actual = fd.readlines() expected_lines = file_contents.split("\n") expected = [l + "\n" if idx != len(expected_lines) - 1 else l for idx, l in enumerate(expected_lines)] assert actual == expected assert int(fd.tell()) == len(file_contents) with pytest.raises(IOError): fd.write(u"Fail") assert fd.closed def test_read_byte_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = b"\x00\x01\x02\x03" expected = "[NtStatus 0xc0000034] No such file or directory" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.open_file(file_path, mode='rb') with smbclient.open_file(file_path, mode='wb') as fd: fd.write(file_contents) with smbclient.open_file(file_path, mode='rb') as fd: assert isinstance(fd, io.BufferedReader) assert fd.closed is False assert fd.name == file_path actual = fd.read() assert actual == file_contents actual = fd.read() assert actual == b"" fd.seek(0) actual = fd.read() assert actual == file_contents with pytest.raises(IOError): fd.write(b"Fail") assert fd.closed def test_write_text_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = u"File Contents\nNewline" with smbclient.open_file(file_path, mode='w') as fd: assert isinstance(fd, io.TextIOWrapper) assert fd.closed is False with pytest.raises(IOError): fd.read() assert fd.tell() == 0 fd.write(file_contents) assert int(fd.tell()) == (len(file_contents) - 1 + len(os.linesep)) assert fd.closed is True with smbclient.open_file(file_path, mode='r') as fd: assert fd.read() == file_contents with smbclient.open_file(file_path, mode='w') as fd: assert fd.tell() == 0 assert fd.write(u"abc") assert fd.tell() == 3 with smbclient.open_file(file_path, mode='r') as fd: assert fd.read() == u"abc" def test_write_byte_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = b"File Contents\nNewline" with smbclient.open_file(file_path, mode='wb') as fd: assert isinstance(fd, io.BufferedWriter) assert fd.closed is False with pytest.raises(IOError): fd.read() assert fd.tell() == 0 fd.write(file_contents) assert fd.tell() == len(file_contents) assert fd.closed is True with smbclient.open_file(file_path, mode='rb') as fd: assert fd.read() == file_contents with smbclient.open_file(file_path, mode='wb') as fd: assert fd.tell() == 0 assert fd.write(b"abc") assert fd.tell() == 3 fd.flush() with smbclient.open_file(file_path, mode='rb') as fd: assert fd.read() == b"abc" # https://github.com/jborean93/smbprotocol/issues/20 def test_read_large_text_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = u"a" * 131074 with smbclient.open_file(file_path, mode='w') as fd: fd.write(file_contents) with smbclient.open_file(file_path) as fd: actual = fd.read() assert len(actual) == 131074 def test_write_exclusive_text_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = u"File Contents\nNewline" with smbclient.open_file(file_path, mode='x') as fd: assert isinstance(fd, io.TextIOWrapper) assert fd.closed is False with pytest.raises(IOError): fd.read() assert fd.tell() == 0 fd.write(file_contents) assert int(fd.tell()) == (len(file_contents) - 1 + len(os.linesep)) assert fd.closed is True with smbclient.open_file(file_path, mode='r') as fd: assert fd.read() == file_contents with pytest.raises(OSError, match=re.escape("[NtStatus 0xc0000035] File exists: ")): smbclient.open_file(file_path, mode='x') assert fd.closed is True def test_write_exclusive_byte_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") file_contents = b"File Contents\nNewline" with smbclient.open_file(file_path, mode='xb') as fd: assert isinstance(fd, io.BufferedWriter) assert fd.closed is False with pytest.raises(IOError): fd.read() assert fd.tell() == 0 fd.write(file_contents) assert fd.tell() == len(file_contents) assert fd.closed is True with smbclient.open_file(file_path, mode='rb') as fd: assert fd.read() == file_contents with pytest.raises(OSError, match=re.escape("[NtStatus 0xc0000035] File exists: ")): smbclient.open_file(file_path, mode='xb') assert fd.closed is True def test_append_text_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='a') as fd: assert isinstance(fd, io.TextIOWrapper) with pytest.raises(IOError): fd.read() fd.write(u"abc") assert fd.tell() == 3 with smbclient.open_file(file_path, mode='a') as fd: assert fd.tell() == 3 fd.write(u"def") assert fd.tell() == 6 with smbclient.open_file(file_path, mode='r') as fd: assert fd.read() == u"abcdef" def test_append_byte_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='ab') as fd: assert isinstance(fd, io.BufferedWriter) with pytest.raises(IOError): fd.read() fd.write(b"abc") assert fd.tell() == 3 with smbclient.open_file(file_path, mode='ab') as fd: assert fd.tell() == 3 fd.write(b"def") assert fd.tell() == 6 with smbclient.open_file(file_path, mode='rb') as fd: assert fd.read() == b"abcdef" def test_read_write_text_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='w+') as fd: fd.write(u"abc") assert fd.tell() == 3 assert fd.read() == u"" fd.seek(0) assert fd.read() == u"abc" def test_read_write_byte_file(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='bw+') as fd: fd.write(b"abc") assert fd.tell() == 3 assert fd.read() == b"" fd.seek(0) assert fd.read() == b"abc" def test_open_directory_fail(smb_share): dir_path = "%s\\%s" % (smb_share, "dir") smbclient.mkdir(dir_path) with pytest.raises(OSError, match=re.escape("[NtStatus 0xc00000ba] Is a directory: ")): smbclient.open_file(dir_path) def test_open_directory_with_correct_file_type(smb_share): dir_name = "%s\\dir" % smb_share smbclient.mkdir(dir_name) with smbclient.open_file(dir_name, mode='rb', buffering=0, file_type='dir') as fd: assert isinstance(fd, SMBDirectoryIO) assert fd.readable() is False assert fd.writable() is False assert fd.seekable() is False def test_open_file_in_missing_dir(smb_share): file_path = "%s\\dir\\%s" % (smb_share, "file.txt") with pytest.raises(OSError, match=re.escape("[NtStatus 0xc000003a] No such file or directory: ")): smbclient.open_file(file_path) def test_open_file_with_read_share_access(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='w') as fd: fd.write(u"contents") with smbclient.open_file(file_path): expected = "[NtStatus 0xc0000043] The process cannot access the file because it is being used by " \ "another process" with pytest.raises(OSError, match=re.escape(expected)): smbclient.open_file(file_path) with smbclient.open_file(file_path, share_access='r') as fd: assert fd.read() == u"contents" with smbclient.open_file(file_path, share_access='r') as fd_child: assert fd_child.read() == u"contents" with pytest.raises(OSError): smbclient.open_file(file_path, mode='a') def test_open_file_with_write_share_access(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='w') as fd: expected = "[NtStatus 0xc0000043] The process cannot access the file because it is being used by " \ "another process: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.open_file(file_path, mode='a') with smbclient.open_file(file_path, mode='w', share_access='w') as fd: fd.write(u"contents") fd.flush() with pytest.raises(OSError): smbclient.open_file(file_path, mode='r') with smbclient.open_file(file_path, mode='a', share_access='w') as fd_child: fd_child.write(u"\nnewline") with smbclient.open_file(file_path, mode='r') as fd: assert fd.read() == u"contents\nnewline" def test_open_file_with_read_write_access(smb_share): file_path = "%s\\%s" % (smb_share, "file.txt") with smbclient.open_file(file_path, mode='w', share_access='rw') as fd: fd.write(u"content") fd.flush() with smbclient.open_file(file_path, mode='a', share_access='rw') as fd_child: fd_child.write(u"\nnewline") with smbclient.open_file(file_path, mode='r', share_access='rw') as fd_child: assert fd_child.read() == u"content\nnewline" def test_open_file_invalid_share_access(smb_share): with pytest.raises(ValueError, match=re.escape("Invalid share_access char z, can only be d, r, w")): smbclient.open_file(smb_share, share_access='z') def test_open_file_invalid_mode_char(smb_share): with pytest.raises(ValueError, match=re.escape("Invalid mode char z, can only be +, a, b, r, t, w, x")): smbclient.open_file(smb_share, mode='z') def test_open_file_invalid_mode(smb_share): with pytest.raises(ValueError, match=re.escape("Invalid mode value b, must contain at least r, w, x, or a")): smbclient.open_file(smb_share, mode='b') def test_open_file_case_sensitive(smb_share): filename = "%s\\File.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"content") assert smbclient.listdir(smb_share) == ["File.txt"] def test_open_file_unbuffered(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='wb', buffering=0) as fd: assert isinstance(fd, SMBFileIO) fd.write(b"abc") fd.flush() with smbclient.open_file(filename, mode='rb', buffering=0) as fd: assert isinstance(fd, SMBFileIO) assert fd.read() == b"abc" def test_open_file_unbuffered_text_file(smb_share): expected = "can't have unbuffered text I/O" with pytest.raises(ValueError, match=re.escape(expected)): smbclient.open_file("%s\\file.txt" % smb_share, mode='w', buffering=0) def test_open_file_with_ads(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"default") with smbclient.open_file(filename + ":ads", mode='w') as fd: fd.write(u"ads") with smbclient.open_file(filename) as fd: assert fd.read() == u"default" with smbclient.open_file(filename + ":ads") as fd: assert fd.read() == u"ads" assert smbclient.listdir(smb_share) == ["file.txt"] with smbclient.open_file(filename, buffering=0, mode='rb') as fd, SMBFileTransaction(fd) as trans: query_info(trans, FileStreamInformation, output_buffer_length=1024) actual = sorted([s['stream_name'].get_value() for s in trans.results[0]]) assert actual == [u"::$DATA", u":ads:$DATA"] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_open_symlink_file(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) with smbclient.open_file(dst_filename) as fd: assert fd.read() == u"content" @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_open_file_in_symlink_dir(smb_share): filename = "%s\\link\\file.txt" % smb_share smbclient.mkdir("%s\\dir" % smb_share) with smbclient.open_file("%s\\dir\\file.txt" % smb_share, mode='w') as fd: fd.write(u"content") smbclient.symlink("%s\\dir" % smb_share, "%s\\link" % smb_share) with smbclient.open_file(filename) as fd: assert fd.read() == u"content" @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_readlink_that_is_normal_file(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share smbclient.symlink(src_filename, dst_filename) actual = smbclient.readlink(dst_filename) assert ntpath.normcase(actual) == ntpath.normcase(src_filename) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_readlink_that_is_normal_dir(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.symlink(src_dirname, dst_dirname, target_is_directory=True) actual = smbclient.readlink(dst_dirname) assert ntpath.normcase(actual) == ntpath.normcase(src_dirname) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_readlink_relative_path(smb_share): src_filename = "%s\\dir1\\file.txt" % smb_share dst_filename = "%s\\dir2\\link.txt" % smb_share smbclient.mkdir("%s\\dir2" % smb_share) smbclient.symlink("..\\dir1\\file.txt", dst_filename) actual = smbclient.readlink(dst_filename) assert ntpath.normcase(actual) == ntpath.normcase(src_filename) def test_readlink_normal_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"content") expected = "[NtStatus 0xc0000275] The file or directory is not a reparse point" with pytest.raises(OSError, match=re.escape(expected)): smbclient.readlink(filename) def test_readlink_not_symlink(monkeypatch): def a(*args, **kwargs): buffer = ReparseDataBuffer() buffer['reparse_tag'] = 1 buffer['data_buffer'] = b"" return buffer monkeypatch.setattr(smbclient._os, "_get_reparse_point", a) expected = "Cannot read link of reparse point with tag (1) IO_REPARSE_TAG_RESERVED_ONE at 'path'" with pytest.raises(ValueError, match=re.escape(expected)): smbclient.readlink("path") def test_remove_file(smb_share): filename = "%s\\delete-me.txt" % smb_share with smbclient.open_file(filename, mode='wb') as fd: fd.write(b"Content") assert smbclient.listdir(smb_share) == ['delete-me.txt'] smbclient.remove(filename) assert smbclient.listdir(smb_share) == [] def test_remove_file_that_is_opened(smb_share): filename = "%s\\delete-me.txt" % smb_share with smbclient.open_file(filename, mode='wb', share_access='d') as fd: fd.write(b"Content") assert smbclient.listdir(smb_share) == ['delete-me.txt'] # Remove the file but because the file is opened by another process (us) it won't be deleted straight away smbclient.remove(filename) assert smbclient.listdir(smb_share) == ['delete-me.txt'] # After closing our handle we can then verify the file was deleted assert smbclient.listdir(smb_share) == [] def test_remove_file_that_is_opened_without_delete_access(smb_share): filename = "%s\\delete-me.txt" % smb_share with smbclient.open_file(filename, mode='wb') as fd: fd.write(b"Content") assert smbclient.listdir(smb_share) == ['delete-me.txt'] # Because our other handle does not have the d share_access set, this should fail expected = "The process cannot access the file because it is being used by another process" with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.remove(filename) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_remove_symlink_missing_src(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share smbclient.symlink(src_filename, dst_filename) smbclient.remove(dst_filename) assert smbclient.listdir(smb_share) == [] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_remove_symlink_with_src(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) smbclient.remove(dst_filename) assert smbclient.listdir(smb_share) == ["file.txt"] def test_removedirs(smb_share): # first create a dir that isn't empty for our basedir that will fail parent_dir = "%s\\directory" % smb_share smbclient.mkdir(parent_dir) with smbclient.open_file("%s\\file.txt" % parent_dir, mode='w') as fd: fd.write(u"content") smbclient.mkdir("%s\\dir1" % parent_dir) smbclient.mkdir("%s\\dir1\\dir2" % parent_dir) smbclient.removedirs("%s\\dir1\\dir2" % parent_dir) assert smbclient.listdir(parent_dir) == ["file.txt"] def test_rename_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"Content") newname = "%s\\file2.txt" % smb_share smbclient.rename(filename, newname) assert smbclient.listdir(smb_share) == ['file2.txt'] def test_rename_folder(smb_share): dirname = "%s\\folder" % smb_share smbclient.mkdir(dirname) newname = "%s\\folder2" % smb_share smbclient.rename(dirname, newname) assert smbclient.listdir(smb_share) == ['folder2'] def test_rename_fail_dst_not_absolute(smb_share): expected = "dst must be an absolute path to where the file or directory should be renamed." with pytest.raises(ValueError, match=re.escape(expected)): smbclient.rename(smb_share, "not_absolute") def test_rename_fail_dst_different_root(smb_share): expected = "Cannot rename a file to a different root than the src." with pytest.raises(ValueError, match=re.escape(expected)): smbclient.rename(smb_share, "\\\\server2\\share\\dst") def test_renames(smb_share): parent_src_dir = "%s\\directory" % smb_share smbclient.mkdir(parent_src_dir) with smbclient.open_file("%s\\file.txt" % parent_src_dir, mode='w') as fd: fd.write(u"content") src_dir = "%s\\subdir\\directory" % parent_src_dir smbclient.makedirs("%s\\sub-folder" % src_dir) with smbclient.open_file("%s\\sub-file.txt" % src_dir, mode='w') as fd: fd.write(u"content") target_dir = "%s\\target" % smb_share smbclient.renames(src_dir, target_dir) assert smbclient.listdir(parent_src_dir) == ['file.txt'] actual = smbclient.listdir(target_dir) assert len(actual) assert 'sub-folder' in actual assert 'sub-file.txt' in actual with smbclient.open_file("%s\\sub-file.txt" % target_dir, mode='r') as fd: assert fd.read() == u"content" def test_replace_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"Content") newname = "%s\\file2.txt" % smb_share smbclient.replace(filename, newname) assert smbclient.listdir(smb_share) == ['file2.txt'] with smbclient.open_file(newname, mode='r') as fd: actual = fd.read() assert actual == u"Content" with smbclient.open_file(filename, mode='w') as fd: fd.write(u"To be replaced") smbclient.replace(newname, filename) assert smbclient.listdir(smb_share) == ['file.txt'] with smbclient.open_file(filename, mode='r') as fd: actual = fd.read() assert actual == u"Content" def test_rmdir(smb_share): dir_name = "%s\\directory" % smb_share smbclient.mkdir(dir_name) assert smbclient.listdir(smb_share) == ['directory'] smbclient.rmdir(dir_name) assert smbclient.listdir(smb_share) == [] expected = "[NtStatus 0xc0000034] No such file or directory: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.rmdir(dir_name) def test_rmdir_non_empty_dir(smb_share): dir_name = "%s\\directory" % smb_share smbclient.mkdir(dir_name) with smbclient.open_file("%s\\file.txt" % dir_name, mode='w') as fd: fd.write(u"content") expected = "[NtStatus 0xc0000101] Directory not empty: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.rmdir(dir_name) def test_rmdir_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"content") expected = "[NtStatus 0xc0000103] Not a directory: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.rmdir(filename) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_rmdir_symlink_missing_src(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.symlink(src_dirname, dst_dirname, target_is_directory=True) smbclient.rmdir(dst_dirname) assert smbclient.listdir(smb_share) == [] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_rmdir_symlink_with_src(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.mkdir(src_dirname) smbclient.symlink(src_dirname, dst_dirname) smbclient.rmdir(dst_dirname) assert smbclient.listdir(smb_share) == ['dir'] def test_scandir_large(smb_share): dir_path = ntpath.join(smb_share, 'directory') # Create lots of directories with the maximum name possible to ensure they won't be returned in 1 request. smbclient.mkdir(dir_path) for i in range(150): dirname = str(i).zfill(255) smbclient.mkdir(ntpath.join(smb_share, 'directory', dirname)) actual = [] for entry in smbclient.scandir(dir_path): actual.append(entry.path) # Just a test optimisation, remove all the dirs so we don't have to re-enumerate them again in rmtree. for path in actual: smbclient.rmdir(path) assert len(actual) == 150 def test_scandir(smb_share): dir_path = ntpath.join(smb_share, 'directory') smbclient.makedirs(dir_path, exist_ok=True) for name in ['file.txt', u'unicode †[💩].txt']: with smbclient.open_file(ntpath.join(dir_path, name), mode='w') as fd: fd.write(u"content") for name in ['subdir1', 'subdir2', u'unicode dir †[💩]', 'subdir1\\sub']: smbclient.mkdir(ntpath.join(dir_path, name)) count = 0 names = [] for dir_entry in smbclient.scandir(dir_path): assert isinstance(dir_entry, SMBDirEntry) names.append(dir_entry.name) # Test out dir_entry for specific file and dir examples if dir_entry.name == 'subdir1': assert str(dir_entry) == "<SMBDirEntry: 'subdir1'>" assert dir_entry.is_dir() is True assert dir_entry.is_file() is False assert dir_entry.stat(follow_symlinks=False).st_ino == dir_entry.inode() assert dir_entry.stat().st_ino == dir_entry.inode() elif dir_entry.name == 'file.txt': assert str(dir_entry) == "<SMBDirEntry: 'file.txt'>" assert dir_entry.is_dir() is False assert dir_entry.is_file() is True assert dir_entry.stat().st_ino == dir_entry.inode() assert dir_entry.stat(follow_symlinks=False).st_ino == dir_entry.inode() assert dir_entry.is_symlink() is False assert dir_entry.inode() is not None assert dir_entry.inode() == dir_entry.stat().st_ino count += 1 assert count == 5 assert u'unicode †[💩].txt' in names assert u'unicode dir †[💩]' in names assert u'subdir2' in names assert u'subdir1' in names assert u'file.txt' in names def test_scamdir_with_pattern(smb_share): for filename in ["file.txt", "file-test1.txt", "file-test1a.txt"]: with smbclient.open_file("%s\\%s" % (smb_share, filename), mode="w") as fd: fd.write(u"content") count = 0 names = [] for dir_entry in smbclient.scandir(smb_share, search_pattern="file-test*.txt"): names.append(dir_entry.name) count += 1 assert count == 2 assert "file-test1.txt" in names assert "file-test1a.txt" in names names = [] for dir_entry in smbclient.scandir(smb_share, search_pattern="file-test?.txt"): names.append(dir_entry.name) assert names == ["file-test1.txt"] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_scandir_with_symlink(smb_share): with smbclient.open_file("%s\\file.txt" % smb_share, mode='w') as fd: fd.write(u"content") smbclient.symlink("%s\\file.txt" % smb_share, "%s\\link.txt" % smb_share) smbclient.mkdir("%s\\dir" % smb_share) smbclient.symlink("%s\\dir" % smb_share, "%s\\link-dir" % smb_share, target_is_directory=True) for entry in smbclient.scandir(smb_share): # This is tested in other tests, we only care about symlinks. if entry.name in ['file.txt', 'dir']: continue assert entry.is_symlink() assert entry.is_dir(follow_symlinks=False) is False assert entry.is_file(follow_symlinks=False) is False if entry.name == 'link.txt': assert entry.is_dir() is False assert entry.is_file() is True else: assert entry.is_dir() is True assert entry.is_file() is False @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_scandir_with_broken_symlink(smb_share): smbclient.symlink("%s\\file.txt" % smb_share, "%s\\link.txt" % smb_share) smbclient.symlink("%s\\dir" % smb_share, "%s\\link-dir" % smb_share, target_is_directory=True) for entry in smbclient.scandir(smb_share): assert entry.is_symlink() assert entry.is_dir() is False assert entry.is_dir(follow_symlinks=False) is False # broken link target assert entry.is_file() is False assert entry.is_file(follow_symlinks=False) is False # broken link target def test_stat_directory(smb_share): actual = smbclient.stat(smb_share) assert isinstance(actual, smbclient.SMBStatResult) assert actual[0] == actual.st_mode assert actual[1] == actual.st_ino assert actual[2] == actual.st_dev assert actual[3] == actual.st_nlink assert actual[4] == actual.st_uid assert actual[5] == actual.st_gid assert actual[6] == actual.st_size assert actual[7] == actual.st_atime assert actual[8] == actual.st_mtime assert actual[9] == actual.st_ctime assert actual[10] == actual.st_chgtime assert actual[11] == actual.st_atime_ns assert actual[12] == actual.st_mtime_ns assert actual[13] == actual.st_ctime_ns assert actual[14] == actual.st_chgtime_ns assert actual[15] == actual.st_file_attributes assert actual[16] == actual.st_reparse_tag assert stat.S_ISDIR(actual.st_mode) assert not stat.S_ISREG(actual.st_mode) assert not stat.S_ISLNK(actual.st_mode) assert actual.st_nlink == 1 assert actual.st_gid == 0 assert actual.st_uid == 0 assert actual.st_size == 0 assert actual.st_ctime is not None assert actual.st_chgtime is not None assert actual.st_atime is not None assert actual.st_mtime is not None assert actual.st_ctime_ns is not None assert actual.st_chgtime_ns is not None assert actual.st_atime_ns is not None assert actual.st_mtime_ns is not None assert actual.st_file_attributes == FileAttributes.FILE_ATTRIBUTE_DIRECTORY assert actual.st_reparse_tag == 0 def test_stat_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"content") actual = smbclient.stat(filename) assert isinstance(actual, smbclient.SMBStatResult) assert actual[0] == actual.st_mode assert actual[1] == actual.st_ino assert actual[2] == actual.st_dev assert actual[3] == actual.st_nlink assert actual[4] == actual.st_uid assert actual[5] == actual.st_gid assert actual[6] == actual.st_size assert actual[7] == actual.st_atime assert actual[8] == actual.st_mtime assert actual[9] == actual.st_ctime assert actual[10] == actual.st_chgtime assert actual[11] == actual.st_atime_ns assert actual[12] == actual.st_mtime_ns assert actual[13] == actual.st_ctime_ns assert actual[14] == actual.st_chgtime_ns assert actual[15] == actual.st_file_attributes assert actual[16] == actual.st_reparse_tag assert not stat.S_ISDIR(actual.st_mode) assert stat.S_ISREG(actual.st_mode) assert not stat.S_ISLNK(actual.st_mode) assert actual.st_nlink == 1 assert actual.st_gid == 0 assert actual.st_uid == 0 assert actual.st_size == 7 assert actual.st_ctime is not None assert actual.st_chgtime is not None assert actual.st_atime is not None assert actual.st_mtime is not None assert actual.st_ctime_ns is not None assert actual.st_chgtime_ns is not None assert actual.st_atime_ns is not None assert actual.st_mtime_ns is not None assert actual.st_file_attributes == FileAttributes.FILE_ATTRIBUTE_ARCHIVE assert actual.st_reparse_tag == 0 def test_stat_readonly(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w', file_attributes=FileAttributes.FILE_ATTRIBUTE_READONLY) as fd: fd.write(u"content") actual = smbclient.stat(filename) assert actual.st_file_attributes == 33 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_stat_symlink_follow(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) actual_src = smbclient.stat(src_filename) actual = smbclient.stat(dst_filename) assert actual.st_ino == actual_src.st_ino @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_stat_symlink_follow_no_target(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share smbclient.symlink(src_filename, dst_filename) expected = "[NtStatus 0xc0000034] No such file or directory: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.stat(dst_filename) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_stat_symlink_dont_follow(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) actual_src = smbclient.stat(src_filename) actual = smbclient.stat(dst_filename, follow_symlinks=False) assert actual.st_ino != actual_src.st_ino assert stat.S_ISLNK(actual.st_mode) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_symlink_file_missing_src(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share smbclient.symlink(src_filename, dst_filename) assert smbclient.listdir(smb_share) == ['link.txt'] actual = smbclient.lstat(dst_filename) assert stat.S_ISLNK(actual.st_mode) assert actual.st_file_attributes == ( FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT | FileAttributes.FILE_ATTRIBUTE_ARCHIVE) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_symlink_file_existing_src(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) actual_files = smbclient.listdir(smb_share) assert 'link.txt' in actual_files assert 'file.txt' in actual_files actual = smbclient.lstat(dst_filename) assert stat.S_ISLNK(actual.st_mode) assert actual.st_file_attributes == ( FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT | FileAttributes.FILE_ATTRIBUTE_ARCHIVE) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_symlink_dir_missing_src(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.symlink(src_dirname, dst_dirname, target_is_directory=True) assert smbclient.listdir(smb_share) == ['link'] actual = smbclient.lstat(dst_dirname) assert stat.S_ISLNK(actual.st_mode) assert actual.st_file_attributes == ( FileAttributes.FILE_ATTRIBUTE_DIRECTORY | FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_symlink_dir_existing_src(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.mkdir(src_dirname) smbclient.symlink(src_dirname, dst_dirname) actual_dirs = smbclient.listdir(smb_share) assert 'link' in actual_dirs assert 'dir' in actual_dirs actual = smbclient.lstat(dst_dirname) assert stat.S_ISLNK(actual.st_mode) assert actual.st_file_attributes == ( FileAttributes.FILE_ATTRIBUTE_DIRECTORY | FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_symlink_relative_src(smb_share): src_filename = "%s\\dir1\\file.txt" % smb_share dst_filename = "%s\\dir2\\link.txt" % smb_share smbclient.mkdir("%s\\dir1" % smb_share) smbclient.mkdir("%s\\dir2" % smb_share) with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink("..\\dir1\\file.txt", dst_filename) with smbclient.open_file(dst_filename) as fd: assert fd.read() == u"content" actual = smbclient.lstat(dst_filename) assert stat.S_ISLNK(actual.st_mode) assert actual.st_file_attributes == ( FileAttributes.FILE_ATTRIBUTE_REPARSE_POINT | FileAttributes.FILE_ATTRIBUTE_ARCHIVE) def test_symlink_fail_not_absolute_dst(smb_share): expected = "The link dst must be an absolute UNC path for where the link is to be created" with pytest.raises(ValueError, match=re.escape(expected)): smbclient.symlink("source", "link") def test_symlink_fail_relative_different_root(smb_share): expected = "Resolved link src root '\\\\server2\\share_name' must be the same as the dst root" with pytest.raises(ValueError, match=re.escape(expected)): smbclient.symlink("\\\\server2\\share_name", smb_share) def test_truncate_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='wb') as fd: fd.write(b"\x01\x02\x03\x04") smbclient.truncate(filename, 2) with smbclient.open_file(filename, mode='rb') as fd: actual = fd.read() assert actual == b"\x01\x02" smbclient.truncate(filename, 4) with smbclient.open_file(filename, mode='rb') as fd: actual = fd.read() assert actual == b"\x01\x02\x00\x00" def test_unlink_file(smb_share): filename = "%s\\delete-me.txt" % smb_share with smbclient.open_file(filename, mode='wb') as fd: fd.write(b"Content") assert smbclient.listdir(smb_share) == ['delete-me.txt'] smbclient.unlink(filename) assert smbclient.listdir(smb_share) == [] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="Samba does not update timestamps") def test_set_utime_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"abc") before_stat = smbclient.stat(filename) smbclient.utime(filename, times=(1, 1)) actual = smbclient.stat(filename) assert actual.st_atime == 1.0 assert actual.st_atime_ns == 1000000000 assert actual.st_ctime == before_stat.st_ctime assert actual.st_ctime_ns == before_stat.st_ctime_ns assert actual.st_mtime == 1.0 assert actual.st_mtime_ns == 1000000000 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="Samba does not update timestamps") def test_set_utime_file_negative(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"abc") before_stat = smbclient.stat(filename) smbclient.utime(filename, times=(-1, -1)) actual = smbclient.stat(filename) assert actual.st_atime == -1.0 assert actual.st_atime_ns == -1000000000 assert actual.st_ctime == before_stat.st_ctime assert actual.st_ctime_ns == before_stat.st_ctime_ns assert actual.st_mtime == -1.0 assert actual.st_mtime_ns == -1000000000 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="Samba does not update timestamps") def test_set_utime_directory(smb_share): dirname = "%s\\directory" % smb_share smbclient.mkdir(dirname) before_stat = smbclient.stat(dirname) smbclient.utime(dirname, times=(1, 1)) actual = smbclient.stat(dirname) assert actual.st_atime == 1.0 assert actual.st_atime_ns == 1000000000 assert actual.st_ctime == before_stat.st_ctime assert actual.st_ctime_ns == before_stat.st_ctime_ns assert actual.st_mtime == 1.0 assert actual.st_mtime_ns == 1000000000 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="Samba does not update timestamps") def test_set_utime_ns(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"abc") before_stat = smbclient.stat(filename) smbclient.utime(filename, ns=(1000000000, 1000000000)) actual = smbclient.stat(filename) assert actual.st_atime == 1.0 assert actual.st_atime_ns == 1000000000 assert actual.st_ctime == before_stat.st_ctime assert actual.st_ctime_ns == before_stat.st_ctime_ns assert actual.st_mtime == 1.0 assert actual.st_mtime_ns == 1000000000 def test_set_utime_both_fail(): with pytest.raises(ValueError, match="Both times and ns have been set for utime"): smbclient.utime("", times=(0, 0), ns=(0, 0)) def test_set_utime_bad_tuple(): expected = "The time tuple should be a 2-tuple of the form (atime, mtime)" with pytest.raises(ValueError, match=re.escape(expected)): smbclient.utime("", times=(0, 0, 0)) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="Samba does not update timestamps") def test_set_utime_touch(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"abc") # Set to EPOCH for baseline smbclient.utime(filename, times=(0, 0)) before_stat = smbclient.stat(filename) smbclient.utime(filename) actual = smbclient.stat(filename) assert actual.st_atime > before_stat.st_atime assert actual.st_atime_ns > before_stat.st_atime_ns assert actual.st_ctime == before_stat.st_ctime assert actual.st_ctime_ns == before_stat.st_ctime_ns assert actual.st_mtime > before_stat.st_mtime assert actual.st_mtime_ns > before_stat.st_mtime_ns @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_set_utime_follow(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) smbclient.utime(dst_filename, times=(1, 1)) actual_link = smbclient.lstat(dst_filename) actual_file = smbclient.lstat(src_filename) assert actual_link.st_atime != 1.0 assert actual_link.st_mtime != 1.0 assert actual_file.st_atime == 1.0 assert actual_file.st_mtime == 1.0 @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_set_utime_dont_follow(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) smbclient.utime(dst_filename, times=(1, 1), follow_symlinks=False) actual_link = smbclient.lstat(dst_filename) actual_file = smbclient.lstat(src_filename) assert actual_link.st_atime == 1.0 assert actual_link.st_mtime == 1.0 assert actual_file.st_atime != 1.0 assert actual_file.st_mtime != 1.0 def test_walk_topdown(smb_share): smbclient.makedirs("%s\\dir1\\dir2\\dir3" % smb_share) for name in ["file1.txt", "dir1\\file2.txt", "dir1\\dir2\\file3.txt", "dir1\\dir2\\dir3\\file4.txt"]: with smbclient.open_file("%s\\%s" % (smb_share, name), mode='w') as fd: fd.write(u"content") scanned_files = [] scanned_dirs = [] for root, dirs, files in smbclient.walk(smb_share): scanned_dirs.append(dirs[0]) # Test out removing a dir entry will affect the further walks. if files == ['file3.txt']: del dirs[0] scanned_files.append(files[0]) assert scanned_files == ['file1.txt', 'file2.txt', 'file3.txt'] assert scanned_dirs == ['dir1', 'dir2', 'dir3'] def test_walk_bottomup(smb_share): smbclient.makedirs("%s\\dir1\\dir2\\dir3" % smb_share) for name in ["file1.txt", "dir1\\file2.txt", "dir1\\dir2\\file3.txt", "dir1\\dir2\\dir3\\file4.txt"]: with smbclient.open_file("%s\\%s" % (smb_share, name), mode='w') as fd: fd.write(u"content") scanned_files = [] scanned_dirs = [] for root, dirs, files in smbclient.walk(smb_share, topdown=False): if dirs: scanned_dirs.append(dirs[0]) scanned_files.append(files[0]) assert scanned_files == ['file4.txt', 'file3.txt', 'file2.txt', 'file1.txt'] assert scanned_dirs == ['dir3', 'dir2', 'dir1'] def test_walk_no_dir(smb_share): fake_dir = "%s\\fake-dir" % smb_share had_result = False for _ in smbclient.walk(fake_dir): had_result = True assert not had_result def test_walk_no_dir_on_error(smb_share): fake_dir = "%s\\fake-dir" % smb_share def on_error(err): raise err expected = "[Error 2] [NtStatus 0xc0000034] No such file or directory: " with pytest.raises(SMBOSError, match=re.escape(expected)): for _ in smbclient.walk(fake_dir, onerror=on_error): pass @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_walk_with_symlink_follow(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.mkdir(src_dirname) smbclient.symlink(src_dirname, dst_dirname) with smbclient.open_file("%s\\file.txt" % src_dirname, mode='w') as fd: fd.write(u"content") scanned_roots = {} for root, dirs, files in smbclient.walk(smb_share, follow_symlinks=True): scanned_roots[root] = { 'dirs': dirs, 'files': files } assert len(scanned_roots) == 3 assert 'dir' in scanned_roots[smb_share]['dirs'] assert 'link' in scanned_roots[smb_share]['dirs'] assert scanned_roots[smb_share]['files'] == [] assert scanned_roots[src_dirname]['dirs'] == [] assert scanned_roots[src_dirname]['files'] == ['file.txt'] assert scanned_roots[dst_dirname]['dirs'] == [] assert scanned_roots[dst_dirname]['files'] == ['file.txt'] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_walk_with_symlink_dont_follow(smb_share): src_dirname = "%s\\dir" % smb_share dst_dirname = "%s\\link" % smb_share smbclient.mkdir(src_dirname) smbclient.symlink(src_dirname, dst_dirname) with smbclient.open_file("%s\\file.txt" % src_dirname, mode='w') as fd: fd.write(u"content") scanned_roots = {} for root, dirs, files in smbclient.walk(smb_share): scanned_roots[root] = { 'dirs': dirs, 'files': files } assert len(scanned_roots) == 2 assert 'dir' in scanned_roots[smb_share]['dirs'] assert 'link' in scanned_roots[smb_share]['dirs'] assert scanned_roots[smb_share]['files'] == [] assert scanned_roots[src_dirname]['dirs'] == [] assert scanned_roots[src_dirname]['files'] == ['file.txt'] def test_xattr_file(smb_share): filename = "%s\\file.txt" % smb_share with smbclient.open_file(filename, mode='w') as fd: fd.write(u"content") assert smbclient.listxattr(filename) == [] smbclient.setxattr(filename, b"KEY", b"VALUE") assert smbclient.listxattr(filename) == [b"KEY"] assert smbclient.getxattr(filename, b"KEY") == b"VALUE" smbclient.removexattr(filename, b"KEY") assert smbclient.listxattr(filename) == [] smbclient.setxattr(filename, b"KEY", b"VALUE") expected = "[NtStatus 0xc0000011]" with pytest.raises(OSError, match=re.escape(expected)): smbclient.getxattr(filename, b"MISSING") expected = "[NtStatus 0xc0000035] File exists: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.setxattr(filename, b"KEY", b"VALUE", smbclient.XATTR_CREATE) expected = "NtStatus 0xc0000034] No such file or directory: " with pytest.raises(OSError, match=re.escape(expected)): smbclient.setxattr(filename, b"MISSING", b"VALUE", smbclient.XATTR_REPLACE) smbclient.setxattr(filename, b"NEW", b"VALUE", smbclient.XATTR_CREATE) assert smbclient.getxattr(filename, b"NEW") == b"VALUE" smbclient.setxattr(filename, b"NEW", b"REPLACE", smbclient.XATTR_REPLACE) assert smbclient.getxattr(filename, b"NEW") == b"REPLACE" assert smbclient.listxattr(filename) == [b"KEY", b"NEW"] def test_xattr_missing_file(smb_share): filename = "%s\\file.txt" % smb_share expected = "[Error 2] [NtStatus 0xc0000034] No such file or directory: " with pytest.raises(SMBOSError, match=re.escape(expected)): smbclient.listxattr(filename) @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_xattr_follow(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) smbclient.setxattr(dst_filename, b"KEY", b"VALUE") assert smbclient.listxattr(dst_filename) == [b"KEY"] assert smbclient.listxattr(dst_filename, follow_symlinks=False) == [] assert smbclient.listxattr(src_filename) == [b"KEY"] assert smbclient.getxattr(dst_filename, b"KEY") == b"VALUE" smbclient.removexattr(dst_filename, b"KEY") assert smbclient.listxattr(dst_filename) == [] @pytest.mark.skipif(os.name != "nt" and not os.environ.get('SMB_FORCE', False), reason="cannot create symlinks on Samba") def test_xattr_dont_follow(smb_share): src_filename = "%s\\file.txt" % smb_share dst_filename = "%s\\link.txt" % smb_share with smbclient.open_file(src_filename, mode='w') as fd: fd.write(u"content") smbclient.symlink(src_filename, dst_filename) smbclient.setxattr(dst_filename, b"KEY", b"VALUE", follow_symlinks=False) assert smbclient.listxattr(dst_filename) == [] assert smbclient.listxattr(dst_filename, follow_symlinks=False) == [b"KEY"] assert smbclient.listxattr(src_filename) == [] assert smbclient.getxattr(dst_filename, b"KEY", follow_symlinks=False) == b"VALUE" smbclient.removexattr(dst_filename, b"KEY", follow_symlinks=False) assert smbclient.listxattr(dst_filename, follow_symlinks=False) == []