The branch, master has been updated via 01079b50a94 tests: Run SMB Py bindings tests against testenv with SMBv1-disabled via 7f731d7642d tests: Avoid hardcoding domain in test via c95a869e852 tests: Completely replace s4 connection in smb tests via 9a3e640bbaa s3:pylibsmb: Add .deltree() API to SMB py bindings via ea00215d538 s3:pylibsmb: Minor refactor to py_cli_list() variables via 1d0d1a758ba s3:libsmb: Avoid duplicated code by making cli_read_sink() public via d7c7d6203ba s3:pylibsmb: Add .loadfile() API to SMB py bindings via 0af78faf5c9 s3:pylibsmb: Add .savefile() API to SMB py bindings via e4d1d53597a s3:pylibsmb: Free async .list() memory via 6bcd64a4f14 s3:pylibsmb: Make s3 and s4 listings return the same dict via 5f1ed29d6b7 s3:pylibsmb: Don't return '.'/'..' in .list() via 3ac4866d15a s3:pylibsmb: Make .list() work for SMBv2 via 057161039f4 s3:pylibsmb: Split code out into do_listing() helper via 2033208a4a9 s3:pylibsmb: Split out code into list_helper() via 40f4e52732f s3:pylibsmb: Consolidate .readdir()/.list() py bindings API via c4dee3437af s3:pylibsmb: Add .chkpath() API to SMB py bindings via 6a40e538df5 s3:pylibsmb: Add .mkdir(), .rmdir() APIS to SMB py bindings via 8096419c712 s3:pylibsmb: Add .unlink() API to SMB Py bindings via 6fff2c26dac s3:pylibsmb: Make lp a mandatory param for the SMB connection from b7d350b45bc s3:smbd: Fix build on AIX
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 01079b50a9413d85eb8295159f036248bdd0edf2 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 14:50:53 2018 +1300 tests: Run SMB Py bindings tests against testenv with SMBv1-disabled Sanity-check that the SMBv2 connection actually works by running it against a testenv with SMBv1 disabled. I've dropped 'local' from the ad_dc target, because it shouldn't be needed. We're trying to test the client-side SMB connection, so running it without 'local' is probably a better test. Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Autobuild-User(master): Tim Beale <timbe...@samba.org> Autobuild-Date(master): Mon Jan 7 04:29:51 CET 2019 on sn-devel-144 commit 7f731d7642de3b1e4cbe43dcd354f0e9dc902787 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 10:04:09 2018 +1300 tests: Avoid hardcoding domain in test Currently the sysvol domain directory is hard-coded, so the tests can only ever run on the ad_dc. This patch makes things marginally better by using the REALM environmental variable instead. This allows us to run it against other testenvs (like the SMBv2-only restoredc). BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit c95a869e85201f836c1579e0f50021e3ffc4cbe0 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 14:42:30 2018 +1300 tests: Completely replace s4 connection in smb tests This test now uses the s3 python bindings completely, so we can remove the s4 connection. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 9a3e640bbaa45f2b6cd2e9a2ff514fdfa26759d0 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 13:45:46 2018 +1300 s3:pylibsmb: Add .deltree() API to SMB py bindings This basically re-uses the underlying functionality of existing APIs in order to support a .deltree() API, i.e. - we use the .list() functionality (i.e. do_listing()) to traverse every item in the given directory. - we then use either .unlink() (i.e. unlink_file()) or .rmdir() (i.e. remove_dir()) to delete the individual item. - sub-directories are handled recursively, by repeating the process. Note that the .deltree() API is currently only really used for testing (and deleting GPO files). So the recursion is never going to be excessive. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ea00215d53891215e20531f75a7ec7e5dec3df5e Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 13:47:46 2018 +1300 s3:pylibsmb: Minor refactor to py_cli_list() variables Add a define for the file attribute mask (we'll reuse it in a subsequent patch), and make the variable type explicit. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 1d0d1a758bacaf160f320f39156923ab4c273fd5 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Thu Jan 3 17:48:39 2019 +1300 s3:libsmb: Avoid duplicated code by making cli_read_sink() public cli_read_sink() and pull_helper() were essentially identical. By making cli_read_sink() non-static, we can delete the latter. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit d7c7d6203ba85785d23481f3926aa6ddb29a7311 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 5 15:08:09 2018 +1300 s3:pylibsmb: Add .loadfile() API to SMB py bindings Add a .loadfile API to read a file's contents. This provides a convenient way to read a file and is consistent with the existing source4 API, which is used by things like the GPO python code and the ntacls backup. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 0af78faf5c9424514ba5bc0baa18701c65c884e7 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Tue Dec 11 09:34:42 2018 +1300 s3:pylibsmb: Add .savefile() API to SMB py bindings This provides a simple API for writing a file's contents and makes the s3 API consistent with the s4 API. All the async APIs here support SMBv2 so we don't need to use the sync APIs at all. Note that we have the choice here of using either cli_write_send() or cli_push_send(). I chose the latter, because that's what smbclient uses. It also appears to handle writing a large file better (i.e. one that exceeds the max write size of the underlying connection). BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit e4d1d53597ac093d56ad9d528e2e580e626ab359 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 12:23:42 2018 +1300 s3:pylibsmb: Free async .list() memory finfos was being allocated but never freed. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6bcd64a4f14332de3259204b78c5b68988e98b6f Author: Tim Beale <timbe...@catalyst.net.nz> Date: Tue Dec 4 16:25:10 2018 +1300 s3:pylibsmb: Make s3 and s4 listings return the same dict Make the python dictionary generated by the s3 .list() use the same keys as the current source4 dict. The reason for using the source4 dict is that other python code depends on these keys (e.g. ntacls.py), whereas the source3 API is currently unused. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 5f1ed29d6b7388e5ce0ad0023daa39cb8c4cb4dc Author: Tim Beale <timbe...@catalyst.net.nz> Date: Tue Dec 4 16:03:35 2018 +1300 s3:pylibsmb: Don't return '.'/'..' in .list() The source4 .list() API wasn't doing this. This makes source3 and source4 have *almost* equivalent functionality, so we can start usign the source3 API in the tests. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 3ac4866d15af3ddebdfa2d9611fc094bf357bc3e Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 12:08:24 2018 +1300 s3:pylibsmb: Make .list() work for SMBv2 In order for .list() to work on an SMBv2 connection we need to use the synchronous API. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 057161039f45baf4acb582a6a8e6d41e3de317bd Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 11:28:22 2018 +1300 s3:pylibsmb: Split code out into do_listing() helper We'll want to reuse the directory listing function for the deltree API. This patch splits the bulk of the work out into a do_listing() helper function. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 2033208a4a936912343f4dcc23576687a2a82c3a Author: Tim Beale <timbe...@catalyst.net.nz> Date: Tue Dec 4 15:43:53 2018 +1300 s3:pylibsmb: Split out code into list_helper() Refactor out the code so we can re-use it for the synchronous API as well. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 40f4e52732f73b8b7b90d3176e4a4536ef6bc678 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Tue Dec 4 09:15:30 2018 +1300 s3:pylibsmb: Consolidate .readdir()/.list() py bindings API Rename the .readdir API to .list and make the parameters match up with the s4 python bindings, i.e. 'mask' is optional and separate from the directory parameter. Note that nothing uses the .readdir source3 API currently, so it's safe to rename. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit c4dee3437afcf4ccba70cb3fd5848b65fd229781 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Mon Dec 3 16:33:19 2018 +1300 s3:pylibsmb: Add .chkpath() API to SMB py bindings Note that this is checking the existence of *directories*, not *files*. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6a40e538df5ba51355a4c4a9949e66b0893f06c0 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Mon Dec 3 16:27:07 2018 +1300 s3:pylibsmb: Add .mkdir(), .rmdir() APIS to SMB py bindings I kept these separate from the chkpath API because it's a nice way to use the old s4 API in the tests to verify the new s3 API works correctly. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8096419c712a66ee2d577a98dfba108a3bd7d6fe Author: Tim Beale <timbe...@catalyst.net.nz> Date: Mon Dec 3 10:50:19 2018 +1300 s3:pylibsmb: Add .unlink() API to SMB Py bindings Add a basic .unlink() API to the source3 bindings. This is based on the source4 python bindings, but uses the source3 client library APIs. (We use a helper function to do most of the work, because we will need to reuse it in order to support the deltree API). Update the source4 test to use the source3 API. We will gradually convert it over, and then delete the source4 python bindings. Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6fff2c26dacb49da749a4ee8261639f518342573 Author: Tim Beale <timbe...@catalyst.net.nz> Date: Wed Dec 12 10:25:35 2018 +1300 s3:pylibsmb: Make lp a mandatory param for the SMB connection Currently establishing the SMB connection relies on having initialized the global source3 loadparm. This patch makes the lp param mandatory, so that you always have to pass the parameter in when establishing the SMB connection. It also makes the source3 API more consistent with the current source4 API, which will make it easier to replace the source4 version later. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13676 Signed-off-by: Tim Beale <timbe...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> ----------------------------------------------------------------------- Summary of changes: python/samba/tests/dcerpc/raw_testcase.py | 2 +- python/samba/tests/libsmb_samba_internal.py | 2 +- python/samba/tests/smb.py | 91 ++-- selftest/knownfail.d/smb | 2 - source3/libsmb/clireadwrite.c | 6 +- source3/libsmb/proto.h | 1 + source3/libsmb/pylibsmb.c | 623 ++++++++++++++++++++++++++-- source4/selftest/tests.py | 4 +- 8 files changed, 639 insertions(+), 92 deletions(-) delete mode 100644 selftest/knownfail.d/smb Changeset truncated at 500 lines: diff --git a/python/samba/tests/dcerpc/raw_testcase.py b/python/samba/tests/dcerpc/raw_testcase.py index ca1b546a399..3a50ceb5330 100644 --- a/python/samba/tests/dcerpc/raw_testcase.py +++ b/python/samba/tests/dcerpc/raw_testcase.py @@ -42,7 +42,7 @@ class smb_pipe_socket(object): def __init__(self, target_hostname, pipename, creds, impersonation_level, lp): lp3 = s3param.get_context() lp3.load(lp.configfile) - self.smbconn = libsmb_samba_internal.Conn(target_hostname, 'IPC$', + self.smbconn = libsmb_samba_internal.Conn(target_hostname, 'IPC$', lp3, credentials=creds, sign=True) self.smbfid = self.smbconn.create(pipename, DesiredAccess=0x12019f, diff --git a/python/samba/tests/libsmb_samba_internal.py b/python/samba/tests/libsmb_samba_internal.py index 8918d848ea8..ebfed948315 100644 --- a/python/samba/tests/libsmb_samba_internal.py +++ b/python/samba/tests/libsmb_samba_internal.py @@ -60,7 +60,7 @@ class LibsmbTestCase(samba.tests.TestCase): creds.set_password(os.getenv("PASSWORD")) c = libsmb_samba_internal.Conn(os.getenv("SERVER_IP"), "tmp", - creds, multi_threaded=True, + lp, creds, multi_threaded=True, force_smb1=True) mythreads = [] diff --git a/python/samba/tests/smb.py b/python/samba/tests/smb.py index 68936473d33..9700953d57a 100644 --- a/python/samba/tests/smb.py +++ b/python/samba/tests/smb.py @@ -23,15 +23,18 @@ from samba import smb from samba import NTSTATUSError from samba.ntstatus import (NT_STATUS_OBJECT_NAME_NOT_FOUND, NT_STATUS_OBJECT_PATH_NOT_FOUND) +from samba.samba3 import libsmb_samba_internal +from samba.samba3 import param as s3param PY3 = sys.version_info[0] == 3 -addom = 'addom.samba.example.com/' +realm = os.environ.get('REALM') +domain_dir = realm.lower() + '/' test_contents = 'abcd' * 256 utf_contents = u'Süßigkeiten Äpfel ' * 128 test_literal_bytes_embed_nulls = b'\xff\xfe\x14\x61\x00\x00\x62\x63\x64' * 256 binary_contents = b'\xff\xfe' binary_contents = binary_contents + "Hello cruel world of python3".encode('utf8') * 128 -test_dir = os.path.join(addom, 'testing_%d' % random.randint(0, 0xFFFF)) +test_dir = os.path.join(domain_dir, 'testing_%d' % random.randint(0, 0xFFFF)) test_file = os.path.join(test_dir, 'testing').replace('/', '\\') @@ -40,22 +43,25 @@ class SMBTests(samba.tests.TestCase): super(SMBTests, self).setUp() self.server = os.environ["SERVER"] creds = self.insta_creds(template=self.get_credentials()) - self.conn = smb.SMB(self.server, - "sysvol", - lp=self.get_loadparm(), - creds=creds) - self.conn.mkdir(test_dir) + + # create an SMB connection to the server + lp = s3param.get_context() + lp.load(os.getenv("SMB_CONF_PATH")) + self.smb_conn = libsmb_samba_internal.Conn(self.server, "sysvol", + lp, creds) + + self.smb_conn.mkdir(test_dir) def tearDown(self): super(SMBTests, self).tearDown() try: - self.conn.deltree(test_dir) + self.smb_conn.deltree(test_dir) except: pass def test_list(self): # check a basic listing returns the items we expect - ls = [f['name'] for f in self.conn.list(addom)] + ls = [f['name'] for f in self.smb_conn.list(domain_dir)] self.assertIn('scripts', ls, msg='"scripts" directory not found in sysvol') self.assertIn('Policies', ls, @@ -66,17 +72,17 @@ class SMBTests(samba.tests.TestCase): msg='Current dir (.) found in directory listing') # using a '*' mask should be the same as using no mask - ls_wildcard = [f['name'] for f in self.conn.list(addom, "*")] + ls_wildcard = [f['name'] for f in self.smb_conn.list(domain_dir, "*")] self.assertEqual(ls, ls_wildcard) # applying a mask should only return items that match that mask - ls_pol = [f['name'] for f in self.conn.list(addom, "Pol*")] + ls_pol = [f['name'] for f in self.smb_conn.list(domain_dir, "Pol*")] expected = ["Policies"] self.assertEqual(ls_pol, expected) # each item in the listing is a has with expected keys expected_keys = ['attrib', 'mtime', 'name', 'short_name', 'size'] - for item in self.conn.list(addom): + for item in self.smb_conn.list(domain_dir): for key in expected_keys: self.assertIn(key, item, msg="Key '%s' not in listing '%s'" % (key, item)) @@ -87,15 +93,16 @@ class SMBTests(samba.tests.TestCase): dirpaths = [] empty_dirs = [] cur_dir = test_dir + for subdir in ["subdir-X", "subdir-Y", "subdir-Z"]: path = self.make_sysvol_path(cur_dir, subdir) - self.conn.mkdir(path) + self.smb_conn.mkdir(path) dirpaths.append(path) cur_dir = path # create another empty dir just for kicks path = self.make_sysvol_path(cur_dir, "another") - self.conn.mkdir(path) + self.smb_conn.mkdir(path) empty_dirs.append(path) # create some files in these directories @@ -104,12 +111,12 @@ class SMBTests(samba.tests.TestCase): for i in range(1, 4): contents = "I'm file {0} in dir {1}!".format(i, subdir) path = self.make_sysvol_path(subdir, "file-{0}.txt".format(i)) - self.conn.savefile(path, test_contents.encode('utf8')) + self.smb_conn.savefile(path, test_contents.encode('utf8')) filepaths.append(path) # sanity-check these dirs/files exist for subdir in dirpaths + empty_dirs: - self.assertTrue(self.conn.chkpath(subdir), + self.assertTrue(self.smb_conn.chkpath(subdir), "Failed to create {0}".format(subdir)) for path in filepaths: self.assertTrue(self.file_exists(path), @@ -117,22 +124,22 @@ class SMBTests(samba.tests.TestCase): # try using deltree to remove a single empty directory path = empty_dirs.pop(0) - self.conn.deltree(path) - self.assertFalse(self.conn.chkpath(path), + self.smb_conn.deltree(path) + self.assertFalse(self.smb_conn.chkpath(path), "Failed to delete {0}".format(path)) # try using deltree to remove a single file path = filepaths.pop(0) - self.conn.deltree(path) + self.smb_conn.deltree(path) self.assertFalse(self.file_exists(path), "Failed to delete {0}".format(path)) # delete the top-level dir - self.conn.deltree(test_dir) + self.smb_conn.deltree(test_dir) # now check that all the dirs/files are no longer there for subdir in dirpaths + empty_dirs: - self.assertFalse(self.conn.chkpath(subdir), + self.assertFalse(self.smb_conn.chkpath(subdir), "Failed to delete {0}".format(subdir)) for path in filepaths: self.assertFalse(self.file_exists(path), @@ -141,7 +148,7 @@ class SMBTests(samba.tests.TestCase): def file_exists(self, filepath): """Returns whether a regular file exists (by trying to open it)""" try: - self.conn.loadfile(filepath) + self.smb_conn.loadfile(filepath) exists = True; except NTSTATUSError as err: if (err.args[0] == NT_STATUS_OBJECT_NAME_NOT_FOUND or @@ -157,72 +164,72 @@ class SMBTests(samba.tests.TestCase): """ # create the test file self.assertFalse(self.file_exists(test_file)) - self.conn.savefile(test_file, binary_contents) + self.smb_conn.savefile(test_file, binary_contents) self.assertTrue(self.file_exists(test_file)) # delete it and check that it's gone - self.conn.unlink(test_file) + self.smb_conn.unlink(test_file) self.assertFalse(self.file_exists(test_file)) def test_chkpath(self): """Tests .chkpath determines whether or not a directory exists""" - self.assertTrue(self.conn.chkpath(test_dir)) + self.assertTrue(self.smb_conn.chkpath(test_dir)) # should return False for a non-existent directory bad_dir = self.make_sysvol_path(test_dir, 'dont_exist') - self.assertFalse(self.conn.chkpath(bad_dir)) + self.assertFalse(self.smb_conn.chkpath(bad_dir)) # should return False for files (because they're not directories) - self.conn.savefile(test_file, binary_contents) - self.assertFalse(self.conn.chkpath(test_file)) + self.smb_conn.savefile(test_file, binary_contents) + self.assertFalse(self.smb_conn.chkpath(test_file)) # check correct result after creating and then deleting a new dir new_dir = self.make_sysvol_path(test_dir, 'test-new') - self.conn.mkdir(new_dir) - self.assertTrue(self.conn.chkpath(new_dir)) - self.conn.rmdir(new_dir) - self.assertFalse(self.conn.chkpath(new_dir)) + self.smb_conn.mkdir(new_dir) + self.assertTrue(self.smb_conn.chkpath(new_dir)) + self.smb_conn.rmdir(new_dir) + self.assertFalse(self.smb_conn.chkpath(new_dir)) def test_save_load_text(self): - self.conn.savefile(test_file, test_contents.encode('utf8')) + self.smb_conn.savefile(test_file, test_contents.encode('utf8')) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents.decode('utf8'), test_contents, msg='contents of test file did not match what was written') # check we can overwrite the file with new contents new_contents = 'wxyz' * 128 - self.conn.savefile(test_file, new_contents.encode('utf8')) - contents = self.conn.loadfile(test_file) + self.smb_conn.savefile(test_file, new_contents.encode('utf8')) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents.decode('utf8'), new_contents, msg='contents of test file did not match what was written') # with python2 this will save/load str type (with embedded nulls) # with python3 this will save/load bytes type def test_save_load_string_bytes(self): - self.conn.savefile(test_file, test_literal_bytes_embed_nulls) + self.smb_conn.savefile(test_file, test_literal_bytes_embed_nulls) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents, test_literal_bytes_embed_nulls, msg='contents of test file did not match what was written') # python3 only this will save/load unicode def test_save_load_utfcontents(self): if PY3: - self.conn.savefile(test_file, utf_contents.encode('utf8')) + self.smb_conn.savefile(test_file, utf_contents.encode('utf8')) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents.decode('utf8'), utf_contents, msg='contents of test file did not match what was written') # with python2 this will save/load str type # with python3 this will save/load bytes type def test_save_binary_contents(self): - self.conn.savefile(test_file, binary_contents) + self.smb_conn.savefile(test_file, binary_contents) - contents = self.conn.loadfile(test_file) + contents = self.smb_conn.loadfile(test_file) self.assertEquals(contents, binary_contents, msg='contents of test file did not match what was written') diff --git a/selftest/knownfail.d/smb b/selftest/knownfail.d/smb deleted file mode 100644 index 3c6324ac7b3..00000000000 --- a/selftest/knownfail.d/smb +++ /dev/null @@ -1,2 +0,0 @@ -# currently savefile appends rather than overwriting -samba.tests.smb.*samba.tests.smb.SMBTests.test_save_load_text\(ad_dc:local\) diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index e953fa5e228..da188db7c31 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -822,7 +822,11 @@ NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received) return NT_STATUS_OK; } -static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) +/* + * Helper function for cli_pull(). This takes a chunk of data (buf) read from + * a remote file and copies it into the return buffer (priv). + */ +NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) { char **pbuf = (char **)priv; memcpy(*pbuf, buf, n); diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h index bfad4dcc011..b0cfcb5aa90 100644 --- a/source3/libsmb/proto.h +++ b/source3/libsmb/proto.h @@ -840,6 +840,7 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, off_t start_offset, off_t size, size_t window_size, NTSTATUS (*sink)(char *buf, size_t n, void *priv), void *priv, off_t *received); +NTSTATUS cli_read_sink(char *buf, size_t n, void *priv); struct tevent_req *cli_read_send( TALLOC_CTX *mem_ctx, struct tevent_context *ev, diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c index 041d408e86e..452c30e9490 100644 --- a/source3/libsmb/pylibsmb.c +++ b/source3/libsmb/pylibsmb.c @@ -28,6 +28,10 @@ #include "source4/libcli/util/pyerrors.h" #include "auth/credentials/pycredentials.h" #include "trans2.h" +#include "libsmb/clirap.h" + +#define LIST_ATTRIBUTE_MASK \ + (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN) static PyTypeObject *get_pytype(const char *module, const char *type) { @@ -423,6 +427,7 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args, char *host, *share; PyObject *creds = NULL; struct cli_credentials *cli_creds; + PyObject *py_lp = Py_None; PyObject *py_multi_threaded = Py_False; bool multi_threaded = false; PyObject *py_sign = Py_False; @@ -435,7 +440,7 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args, int flags = 0; static const char *kwlist[] = { - "host", "share", "credentials", + "host", "share", "lp", "credentials", "multi_threaded", "sign", "force_smb1", NULL }; @@ -447,8 +452,8 @@ static int py_cli_state_init(struct py_cli_state *self, PyObject *args, } ret = ParseTupleAndKeywords( - args, kwds, "ss|O!OOO", kwlist, - &host, &share, + args, kwds, "ssO|O!OOO", kwlist, + &host, &share, &py_lp, py_type_Credentials, &creds, &py_multi_threaded, &py_sign, @@ -740,6 +745,92 @@ static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args) Py_RETURN_NONE; } +struct push_state { + char *data; + off_t nread; + off_t total_data; +}; + +/* + * cli_push() helper to write a chunk of data to a remote file + */ +static size_t push_data(uint8_t *buf, size_t n, void *priv) +{ + struct push_state *state = (struct push_state *)priv; + char *curr_ptr = NULL; + off_t remaining; + size_t copied_bytes; + + if (state->nread >= state->total_data) { + return 0; + } + + curr_ptr = state->data + state->nread; + remaining = state->total_data - state->nread; + copied_bytes = MIN(remaining, n); + + memcpy(buf, curr_ptr, copied_bytes); + state->nread += copied_bytes; + return copied_bytes; +} + +/* + * Writes a file with the contents specified + */ +static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args, + PyObject *kwargs) +{ + uint16_t fnum; + const char *filename = NULL; + char *data = NULL; + Py_ssize_t size = 0; + NTSTATUS status; + struct tevent_req *req = NULL; + struct push_state state; + + if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename, + &data, &size)) { + return NULL; + } + + /* create a new file handle for writing to */ + req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0, + FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE, + SMB2_IMPERSONATION_IMPERSONATION, 0); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_ntcreate_recv(req, &fnum, NULL); + TALLOC_FREE(req); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + + /* write the new file contents */ + state.data = data; + state.nread = 0; + state.total_data = size; + + req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0, + push_data, &state); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_push_recv(req); + TALLOC_FREE(req); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + + /* close the file handle */ + req = cli_close_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_close_recv(req); + PyErr_NTSTATUS_IS_ERR_RAISE(status); + + Py_RETURN_NONE; +} + static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args, PyObject *kwds) { @@ -776,6 +867,125 @@ static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args, return Py_BuildValue("K", (unsigned long long)written); } +/* + * Returns the size of the given file + */ +static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum, + off_t *size) +{ + NTSTATUS status; + + if (self->is_smb1) { + uint8_t *rdata = NULL; + struct tevent_req *req = NULL; + + req = cli_qfileinfo_send(NULL, self->ev, self->cli, fnum, + SMB_QUERY_FILE_ALL_INFO, 68, + CLI_BUFFER_SIZE); + if (!py_tevent_req_wait_exc(self, req)) { + return NT_STATUS_INTERNAL_ERROR; + } + status = cli_qfileinfo_recv(req, NULL, NULL, &rdata, NULL); + if (NT_STATUS_IS_OK(status)) { + *size = IVAL2_TO_SMB_BIG_UINT(rdata, 48); + } + TALLOC_FREE(req); + TALLOC_FREE(rdata); + } else { + status = cli_qfileinfo_basic(self->cli, fnum, NULL, size, + NULL, NULL, NULL, NULL, NULL); + } + return status; +} + +/* + * Loads the specified file's contents and returns it + */ +static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args, + PyObject *kwargs) +{ + NTSTATUS status; + const char *filename = NULL; + struct tevent_req *req = NULL; + uint16_t fnum; + off_t size; + char *buf = NULL; + off_t nread = 0; + PyObject *result = NULL; + + if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) { + return NULL; + } + + /* get a read file handle */ + req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0, + FILE_READ_DATA, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, FILE_OPEN, 0, -- Samba Shared Repository